Angular 4.x 动态创建表单
本文将介绍如何动态创建表单组件,我们最终实现的效果如下: 图片描述 在阅读本文之前,请确保你已经掌握 Angular 响应式表单和动态创建组件的相关知识,如果对相关知识还不了解,推荐先阅读一下 Angular 4.x Reactive Forms 和 Angular 4.x 动态创建组件 这两篇文章。对于已掌握的读者,我们直接进入主题。 创建动态表单创建 DynamicFormModule在当前目录先创建 dynamic-form/dynamic-form.module.ts import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { ReactiveFormsModule } from '@angular/forms'; @NgModule({ imports: [ CommonModule,ReactiveFormsModule ] }) export class DynamicFormModule {} 创建完 import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { DynamicFormModule } from './dynamic-form/dynamic-form.module'; import { AppComponent } from './app.component'; @NgModule({ imports: [BrowserModule,DynamicFormModule],declarations: [AppComponent],bootstrap: [AppComponent] }) export class AppModule { } 创建 DynamicForm 容器进入 import { Component,Input,OnInit } from '@angular/core'; import { FormGroup,FormBuilder } from '@angular/forms'; @Component({ selector: 'dynamic-form',template: ` <form [formGroup]="form"> </form> ` }) export class DynamicFormComponent implements OnInit { @Input() config: any[] = []; form: FormGroup; constructor(private fb: FormBuilder) {} ngOnInit() { this.form = this.createGroup(); } createGroup() { const group = this.fb.group({}); this.config.forEach(control => group.addControl(control.name,this.fb.control(''))); return group; } } 由于我们的表单是动态的,我们需要接受一个数组类型的配置对象才能知道需要动态创建的内容。因此,我们定义了一个 此外我们利用了 Angular 响应式表单,提供的 API 动态的创建
在 接下来我们在 DynamicFormModule 模块中声明并导出新建的 import { DynamicFormComponent } from './containers/dynamic-form/dynamic-form.component'; @NgModule({ imports: [ CommonModule,ReactiveFormsModule ],declarations: [ DynamicFormComponent ],exports: [ DynamicFormComponent ] }) export class DynamicFormModule {} 现在我们已经创建了表单,让我们实际使用它。 使用动态表单打开 app.component.ts 文件,在组件模板中引入我们创建的 app.component.ts import { Component } from '@angular/core'; interface FormItemOption { type: string; label: string; name: string; placeholder?: string; options?: string[] } @Component({ selector: 'exe-app',template: ` <div> <dynamic-form [config]="config"></dynamic-form> </div> ` }) export class AppComponent { config: FormItemOption[] = [ { type: 'input',label: 'Full name',name: 'name',placeholder: 'Enter your name' },{ type: 'select',label: 'Favourite food',name: 'food',options: ['Pizza','Hot Dogs','Knakworstje','Coffee'],placeholder: 'Select an option' },{ type: 'button',label: 'Submit',name: 'submit' } ]; } 上面代码中,我们在 AppComponent 组件类中设置了 自定义表单项组件FormInputComponent在 form-input.component.ts import { Component,ViewContainerRef } from '@angular/core'; import { FormGroup } from '@angular/forms'; @Component({ selector: 'form-input',template: ` <div [formGroup]="group"> <label>{{ config.label }}</label> <input type="text" [attr.placeholder]="config.placeholder" [formControlName]="config.name" /> </div> ` }) export class FormInputComponent { config: any; group: FormGroup; } 上面代码中,我们在 FormInputComponent 组件类中定义了 FormSelectComponentimport { Component } from '@angular/core'; import { FormGroup } from '@angular/forms'; @Component({ selector: 'form-select',template: ` <div [formGroup]="group"> <label>{{ config.label }}</label> <select [formControlName]="config.name"> <option value="">{{ config.placeholder }}</option> <option *ngFor="let option of config.options"> {{ option }} </option> </select> </div> ` }) export class FormSelectComponent { config: Object; group: FormGroup; } FormSelectComponent 组件与 FormInputComponent 组件的主要区别是,我们需要循环配置中定义的options属性。这用于向用户显示所有的选项,我们还使用占位符属性,作为默认的选项。 FormButtonComponentimport { Component } from '@angular/core'; import { FormGroup } from '@angular/forms'; @Component({ selector: 'form-button',template: ` <div [formGroup]="group"> <button type="submit"> {{ config.label }} </button> </div> ` }) export class FormButtonComponent{ config: Object; group: FormGroup; } 以上代码,我们只是定义了一个简单的按钮,它使用 // ... import { FormButtonComponent } from './components/form-button/form-button.component'; import { FormInputComponent } from './components/form-input/form-input.component'; import { FormSelectComponent } from './components/form-select/form-select.component'; @NgModule({ // ... declarations: [ DynamicFormComponent,FormButtonComponent,FormInputComponent,FormSelectComponent ],exports: [ DynamicFormComponent ] }) export class DynamicFormModule {} 到目前为止,我们已经创建了三个组件。若想动态的创建这三个组件,我们将定义一个指令,该指令的功能跟 import { Directive,Input } from '@angular/core'; import { FormGroup } from '@angular/forms'; @Directive({ selector: '[dynamicField]' }) export class DynamicFieldDirective { @Input() config: Object; @Input() group: FormGroup; } 我们将指令的 这样做的好处是,我们的指令可以应用在 Angular 内置的 另外在指令中,我们使用 动态渲染组件,我们需要用到 让我们在 DynamicFieldDirective 指令构造函数中,注入相关对象,具体代码如下: import { ComponentFactoryResolver,Directive,OnInit,ViewContainerRef } from '@angular/core'; import { FormGroup } from '@angular/forms'; @Directive({ selector: '[dynamicField]' }) export class DynamicFieldDirective implements OnInit { @Input() config; @Input() group: FormGroup; constructor( private resolver: ComponentFactoryResolver,private container: ViewContainerRef ) {} ngOnInit() { } } 上面代码中,我们还添加了 // ... 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 { // ... } 这将允许我们通过 // ... const components = { button: FormButtonComponent,select: FormSelectComponent }; @Directive(...) export class DynamicFieldDirective implements OnInit { // ... ngOnInit() { const component = components[this.config.type]; const factory = this.resolver.resolveComponentFactory<any>(component); } // ... } 现在我们引用了配置中定义的给定类型的组件,并将其传递给 ComponentFactoryRsolver 对象提供的 现在我们已经有了组件工厂,我们可以简单地告诉我们的 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); } // ... } 我们现在已经可以将 @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; } // ... } 接下来,让我们在 // ... import { DynamicFieldDirective } from './components/dynamic-field/dynamic-field.directive'; @NgModule({ // ... declarations: [ DynamicFieldDirective,DynamicFormComponent,exports: [ DynamicFormComponent ] }) export class DynamicFormModule {} 如果我们直接在浏览器中运行以上程序,控制台会抛出异常。当我们想要通过 @NgModule({ // ... entryComponents: [ FormButtonComponent,FormSelectComponent ] }) export class DynamicFormModule {} 基本工作都已经完成,现在我们需要做的就是更新 @Component({ selector: 'dynamic-form',template: ` <form class="dynamic-form" [formGroup]="form"> <ng-container *ngFor="let field of config;" dynamicField [config]="field" [group]="form"> </ng-container> </form> ` }) export class DynamicFormComponent implements OnInit { // ... } 正如我们前面提到的,我们使用 此外我们使用 表单提交我们需要做的是为我们的 import { Component,Output,EventEmitter} from '@angular/core'; import { FormGroup,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>(); // ... } 最后我们同步更新一下 import { Component } from '@angular/core'; @Component({ selector: 'exe-app',template: ` <div class="app"> <dynamic-form [config]="config" (submitted)="formSubmitted($event)"> </dynamic-form> </div> ` }) export class AppComponent { // ... formSubmitted(value: any) { console.log(value); } } Toddmotto 大神线上示例 - angular-dynamic-forms,查看完整代码请访问 - toddmott/angular-dynamic-forms。 我有话说在自定义表单控件组件中
|