Angular 2: How to link form elements across a dynamically created components?

懵懂的女人 提交于 2019-12-11 02:13:25

问题


I have a set of form fields that are in a dynamically created component. The parent Component owns the form tag. However, none of the form fields are being added to the Form. I'm using the ComponentFactoryResolver to create the component:

@Component({
selector: 'fieldset-container',
templateUrl: './fieldset-container.component.html',
styleUrls: ['./fieldset-container.component.scss'],
entryComponents: ALL_FIELD_SETS,
})
export class FieldsetContainerComponent<C> {

fieldsetComponent : ComponentRef<any> = null;

@Input() formGroup : FormGroup;

@ViewChild('fieldSetContainer', {read: ViewContainerRef})
fieldsetContainer : ViewContainerRef;

@Output() onComponentCreation = new EventEmitter<ComponentRef<any>>();

constructor(private resolver : ComponentFactoryResolver) {
}

@Input() set fieldset( fieldset : {component : any, resolve : any }) {
    if( !fieldset ) return; // sorry not right

    // Inputs need to be in the following format to be resolved properly
    let inputProviders = Object.keys(fieldset.resolve).map((resolveName) => {return {provide: resolveName, useValue: fieldset.resolve[resolveName]};});
    let resolvedInputs = ReflectiveInjector.resolve(inputProviders);

    // We create an injector out of the data we want to pass down and this components injector
    let injector = ReflectiveInjector.fromResolvedProviders(resolvedInputs, this.fieldsetContainer.parentInjector);


    // We create a factory out of the component we want to create
    let factory = this.resolver.resolveComponentFactory(findComponentForFieldset(fieldset.component));

    // We create the component using the factory and the injector
    let component : ComponentRef<any> = factory.create(injector);

    // We insert the component into the dom container
    this.fieldsetContainer.insert(component.hostView);

    // Destroy the previously created component
    if (this.fieldsetComponent) {
        this.fieldsetComponent.destroy();
    }

    this.fieldsetComponent = component;
    this.onComponentCreation.emit( this.fieldsetComponent );
}
}

The template:

<div #fieldSetContainer [formGroup]="formGroup"></div>

The usage of the dynamic component:

<form class="form" #omaForm="ngForm">
    <div *ngFor="let fieldset of page?.fieldsets">
        <fieldset-container [fieldset]="{ component: fieldset, resolve: {} }" (onComponentCreation)="onComponentCreation($event)" [formGroup]="omaForm.form"></fieldset-container>
    </div>
</form>

I suspect it has something to do with the injector not being hooked up correctly, but from what I can tell it is chained to the parent. I've set a breakpoint in NgModel and it is passed a null for parent which is the problem. I traced that back up into something that looks compiled and it was just hard coding a null. So I'm not sure how that was created with hard coded nulls in there.

Any ideas on how to fix this?


回答1:


Ok it turns out it has nothing to do with the dynamic nature of this component. I removed it and defined all of my components inline and it still had the problem. The issue was that having form controls inside a Component that were nested within a form tag is just not supported by Angular out of the box. Once you nest a form control in a component it can't see the NgForm anymore which is crazy.

After reading solutions on the web and seeing that no one had a good solution I designed 2 of my own directives that registered the Form into the DI container up at the NgForm, then using DI hierarchy I could inject that into another Directive that would perform the registration below.

Parent Component Template:

<form nested>
    <my-component .../>
</form>

Child Component Template:

<div>
   <input name="street" [(ngModel)]="address.street" required nest/>
   <input name="city" [(ngModel)]="address.city" required nest/>
   <input name="state" [(ngModel)]="address.state" required nest/>
   <input name="zip" [(ngModel)]="address.zip" required nest/>
</div>

Once I had this in place then I could bring back my dynamic component and it worked perfectly. It was just really hard to get there.

It's really elegant and simple and doesn't require me to pass the form instance down through the layers like so many suggestions on the web show. And the work to register a form control whether it's 1 layer or 999 layers removed is the same.

IMHO NgForm and NgModel should just do this out of the box for us, but they don't which leads to complicated architecture design to accomplish moderately advanced forms.



来源:https://stackoverflow.com/questions/43399022/angular-2-how-to-link-form-elements-across-a-dynamically-created-components

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!