Angular 4.x 动态创建表单实例(3)

上面代码中,我们还添加了 ngOnInit 生命周期钩子。由于我们允许使用 input 或 select 类型来声明组件的类型,因此我们需要创建一个对象来将字符串映射到相关的组件类,具体如下:

// ... import { FormButtonComponent } from '../form-button/form-button.component'; import { FormInputComponent } from '../form-input/form-input.component'; import { FormSelectComponent } from '../form-select/form-select.component'; const components = { button: FormButtonComponent, input: FormInputComponent, select: FormSelectComponent }; @Directive(...) export class DynamicFieldDirective implements OnInit { // ... }

这将允许我们通过 components['button'] 获取对应的 FormButtonComponent 组件类,然后我们可以把它传递给 ComponentFactoryResolver 对象以获取对应的 ComponentFactory (组件工厂):

// ... const components = { button: FormButtonComponent, input: FormInputComponent, select: FormSelectComponent }; @Directive(...) export class DynamicFieldDirective implements OnInit { // ... ngOnInit() { const component = components[this.config.type]; const factory = this.resolver.resolveComponentFactory<any>(component); } // ... }

现在我们引用了配置中定义的给定类型的组件,并将其传递给 ComponentFactoryRsolver 对象提供的resolveComponentFactory() 方法。您可能已经注意到我们在 resolveComponentFactory 旁边使用了 <any>,这是因为我们要创建不同类型的组件。此外我们也可以定义一个接口,然后每个组件都去实现,如果这样的话 any 就可以替换成我们已定义的接口。

现在我们已经有了组件工厂,我们可以简单地告诉我们的 ViewContainerRef 为我们创建这个组件:

@Directive(...) export class DynamicFieldDirective implements OnInit { // ... component: any; ngOnInit() { const component = components[this.config.type]; const factory = this.resolver.resolveComponentFactory<any>(component); this.component = this.container.createComponent(factory); } // ... }

我们现在已经可以将 config 和 group 传递到我们动态创建的组件中。我们可以通过 this.component.instance 访问到组件类的实例:

@Directive(...) export class DynamicFieldDirective implements OnInit { // ... component; ngOnInit() { const component = components[this.config.type]; const factory = this.resolver.resolveComponentFactory<any>(component); this.component = this.container.createComponent(factory); this.component.instance.config = this.config; this.component.instance.group = this.group; } // ... }

接下来,让我们在 DynamicFormModule 中声明已创建的 DynamicFieldDirective 指令:

// ... import { DynamicFieldDirective } from './components/dynamic-field/dynamic-field.directive'; @NgModule({ // ... declarations: [ DynamicFieldDirective, DynamicFormComponent, FormButtonComponent, FormInputComponent, FormSelectComponent ], exports: [ DynamicFormComponent ] }) export class DynamicFormModule {}

如果我们直接在浏览器中运行以上程序,控制台会抛出异常。当我们想要通过 ComponentFactoryResolver 对象动态创建组件的话,我们需要在 @NgModule 配置对象的一个属性 - entryComponents 中,声明需动态加载的组件。

@NgModule({ // ... entryComponents: [ FormButtonComponent, FormInputComponent, FormSelectComponent ] }) export class DynamicFormModule {}

基本工作都已经完成,现在我们需要做的就是更新 DynamicFormComponent 组件,应用我们之前已经 DynamicFieldDirective 实现动态组件的创建:

@Component({ selector: 'dynamic-form', template: ` <form [formGroup]="form"> <ng-container *ngFor="let field of config;" dynamicField [config]="field" [group]="form"> </ng-container> </form> ` }) export class DynamicFormComponent implements OnInit { // ... }

正如我们前面提到的,我们使用 <ng-container>作为容器来重复我们的动态字段。当我们的组件被渲染时,这是不可见的,这意味着我们只会在 DOM 中看到我们的动态创建的组件。

此外我们使用 *ngFor 结构指令,根据 config (数组配置项) 动态创建组件,并设置 dynamicField 指令的两个输入属性:config 和 group。最后我们需要做的是实现表单提交功能。

表单提交

我们需要做的是为我们的 <form> 组件添加一个 (ngSubmit) 事件的处理程序,并在我们的动态表单组件中新增一个 @Output 输出属性,以便我们可以通知使用它的组件。

import { Component, Input, Output, OnInit, EventEmitter} from '@angular/core'; import { FormGroup, FormBuilder } from '@angular/forms'; @Component({ selector: 'dynamic-form', template: ` <form [formGroup]="form" (ngSubmit)="submitted.emit(form.value)"> <ng-container *ngFor="let field of config;" dynamicField [config]="field" [group]="form"> </ng-container> </form> ` }) export class DynamicFormComponent implements OnInit { @Input() config: any[] = []; @Output() submitted: EventEmitter<any> = new EventEmitter<any>(); // ... }

最后我们同步更新一下 app.component.ts 文件:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wypgxx.html