本文将介绍如何动态创建表单组件,我们最终实现的效果如下:

在阅读本文之前,请确保你已经掌握 Angular 响应式表单和动态创建组件的相关知识,如果对相关知识还不了解,推荐先阅读一下 Angular 4.x Reactive Forms 和 Angular 4.x 动态创建组件 这两篇文章。对于已掌握的读者,我们直接进入主题。
创建动态表单
创建 DynamicFormModule
在当前目录先创建 dynamic-form 目录,然后在该目录下创建 dynamic-form.module.ts 文件,文件内容如下:
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 {}
</div>
创建完 DynamicFormModule 模块,接着我们需要在 AppModule 中导入该模块:
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 { }
</div>
创建 DynamicForm 容器
进入 dynamic-form 目录,在创建完 containers 目录后,继续创建 dynamic-form 目录,然后在该目录创建一个名为 dynamic-form.component.ts 的文件,文件内容如下:
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;
}
}
</div>
由于我们的表单是动态的,我们需要接受一个数组类型的配置对象才能知道需要动态创建的内容。因此,我们定义了一个 config 输入属性,用于接收数组类型的配置对象。
此外我们利用了 Angular 响应式表单,提供的 API 动态的创建 FormGroup 对象。对于配置对象中的每一项,我们要求该项至少包含两个属性,即 (type) 类型和 (name) 名称:
- type - 用于设置表单项的类型,如
input、select、button等 - name - 用于设置表单控件的 name 属性
在 createGroup() 方法中,我们循环遍历输入的 config 属性,然后利用 FormGroup 对象提供的 addControl() 方法,动态地添加新建的表单控件。
接下来我们在 DynamicFormModule 模块中声明并导出新建的 DynamicFormComponent 组件:
import { DynamicFormComponent } from './containers/dynamic-form/dynamic-form.component';
@NgModule({
imports: [
CommonModule,
ReactiveFormsModule
],
declarations: [
DynamicFormComponent
],
exports: [
DynamicFormComponent
]
})
export class DynamicFormModule {}
</div>
现在我们已经创建了表单,让我们实际使用它。
使用动态表单
打开 app.component.ts 文件,在组件模板中引入我们创建的 dynamic-form 组件,并设置相关的配置对象,具体示例如下:
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'
}
];
}
</div>
上面代码中,我们在 AppComponent 组件类中设置了 config 配置对象,该配置对象中设置了三种类型的表单类型。对于每个表单项的配置对象,我们定义了一个 FormItemOption 数据接口,该接口中我们定义了三个必选属性:type、label 和 name 及两个可选属性:options 和 placeholder。下面让我们创建对应类型的组件。
自定义表单项组件
FormInputComponent
在 dynamic-form 目录,我们新建一个 components 目录,然后创建 form-input、form-select 和 form-button 三个文件夹。创建完文件夹后,我们先来定义 form-input 组件:
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;
}
</div>
上面代码中,我们在 FormInputComponent 组件类中定义了 config 和 group 两个属性,但我们并没有使用 @Input 装饰器来定义它们,因为我们不会以传统的方式来使用这个组件。接下来,我们来定义 select 和 button 组件。
FormSelectComponent
import { 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;
}
</div>
FormSelectComponent 组件与 FormInputComponent 组件的主要区别是,我们需要循环配置中定义的options属性。这用于向用户显示所有的选项,我们还使用占位符属性,作为默认的选项。
FormButtonComponent
import { 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;
}
</div>
以上代码,我们只是定义了一个简单的按钮,它使用 config.label 的值作为按钮文本。与所有组件一样,我们需要在前面创建的模块中声明这些自定义组件。打开 dynamic-form.module.ts 文件并添加相应声明:

