继Angular学习笔记46:响应式表单-使用FormBuild快速构建表单,可以使用FormBuilder快速便捷的构建出需要的表单。
有时候,在FormArray中,不仅仅是一个控件,有可能是多个,这个时候,这个FormArray中的元素就是一个FormGroup,并且这个FormGroup中的某一个实例又是一个FormArray,这样FormGroup和FormArray就会产生深层次的嵌套
有时会遇到这样的场景,模拟一个工作流。
现在来实行它~
创建一个表单嵌套组件
wujiayudeMacBook-Pro:demo-test wjy$ ng g c form-nested
CREATE src/app/form-nested/form-nested.component.less (0 bytes)
CREATE src/app/form-nested/form-nested.component.html (30 bytes)
CREATE src/app/form-nested/form-nested.component.spec.ts (657 bytes)
CREATE src/app/form-nested/form-nested.component.ts (289 bytes)
UPDATE src/app/app.module.ts (1627 bytes)
修改模板文件:
<nz-divider [nzText]="'表单嵌套'"></nz-divider>
修改根组件,将这个组件显示出来
<!--<router-outlet></router-outlet>-->
<!--动画-->
<!--<app-animation-demo></app-animation-demo>-->
<!--<app-complex-animations></app-complex-animations>-->
<!--可编辑的table的Demo-->
<!--<app-edit-table></app-edit-table>-->
<!--响应式表单 FormGroup FormArray-->
<!--<app-user-info></app-user-info>-->
<!--多层级的表单嵌套-->
<app-form-nested></app-form-nested>
保存运行:
先构造出最上层(工作流)的实例
修改类文件:
import {Component, OnInit} from '@angular/core';
import {FormBuilder, FormGroup, Validators} from '@angular/forms';
@Component({
selector: 'app-form-nested',
templateUrl: './form-nested.component.html',
styleUrls: ['./form-nested.component.less']
})
export class FormNestedComponent implements OnInit {
public validateForm: FormGroup;
constructor(private fb: FormBuilder) {
this.validateForm = this.fb.group({
workFlowName: [null, [Validators.required]],
workFlowType: [null, [Validators.required]],
workFlowContent: this.fb.array([
this.fb.control(null)
])
});
}
ngOnInit() {
}
}
修改模板文件:
<nz-divider [nzText]="'表单嵌套'"></nz-divider>
<form [formGroup]="validateForm">
<nz-form-item>
<nz-form-label nzSpan="3" nz-col>
工作流名称
</nz-form-label>
<nz-form-control nzSpan="7" nz-col>
<input nz-input type="text" placeholder="请输入工作流名称" formControlName="workFlowName">
</nz-form-control>
<nz-form-label nzSpan="3" nzOffset="1" nz-col>
工作流类型
</nz-form-label>
<nz-form-control nzSpan="7" nz-col>
<input nz-input type="text" placeholder="请输入工作流类型" formControlName="workFlowType">
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label nzSpan="3" nz-col>
工作流内容
</nz-form-label>
<nz-form-control nzSpan="14">
<nz-row formArrayName="workFlowContent"
*ngFor="let content of validateForm.controls['workFlowContent'].controls;
let workFlowIndex = index">
<nz-form-control nz-col nzSpan="12">
<input nz-input [formControlName]="workFlowIndex" type="text">
</nz-form-control>
</nz-row>
</nz-form-control>
</nz-form-item>
</form>
<nz-form-item>
<nz-form-label nzSpan="3" nz-col>
表单的值
</nz-form-label>
<nz-form-control nzSpan="21" nz-col>
{{validateForm.value | json}}
</nz-form-control>
</nz-form-item>
保存运行以后,发现最上层(工作流)的实例已经实现:
构造出工作流内容(workFlowContent)中的的实例
将validateForm中的workFlowContent实例中的this.fb.control(null),转换成this.fb.group({})。
在这个this.fb.group({})中添加workFlowContent(FormGroup的实例).
- 修改workFlowContent(FormGroup的实例):
workFlowContent: this.fb.array([
this.fb.group({
stageName: [null, [Validators.required]],
stageType: [null, [Validators.required]],
stageContent: this.fb.array([
this.fb.control(null)
]),
})
])
现在以:stageName(FormControl)、stageType(FormControl)、stageContent(FormArray)这三个为一个FormGroup作为workFlowContent的其中一个实例。
- 修改模板文件
注意,在这里有两种写法
第一种:将stageName(FormControl)、stageType(FormControl)、stageContent(FormArray)这些组成的 FormGroup 的 FormGroupName用一个div表示,将stageName(FormControl)、stageType(FormControl)、stageContent(FormArray)包在这个div中。
<nz-form-control nzSpan="18">
<div class="workFlowContent" formArrayName="workFlowContent"
*ngFor="let content of validateForm.controls['workFlowContent'].controls;
let workflowIndex = index">
<div [formGroupName]="workflowIndex.toString()">
<nz-row>
<nz-form-label nzSpan="3" nz-col>
stage{{workflowIndex + 1}}
</nz-form-label>
</nz-row>
<nz-row>
<nz-form-label nzSpan="3" nz-col>stageName</nz-form-label>
<nz-form-control nz-col nzSpan="7">
<input nz-input type="text" formControlName="stageName">
</nz-form-control>
<nz-form-label nzOffset="1" nzSpan="3" nz-col>stageType</nz-form-label>
<nz-form-control nz-col nzSpan="7">
<input nz-input type="text" formControlName="stageType">
</nz-form-control>
</nz-row>
<nz-row>
<nz-form-label nzSpan="3" nz-col>stageContent</nz-form-label>
<nz-form-control nz-col nzSpan="18">
<nz-row formArrayName="stageContent"
*ngFor="let stage of content.get('stageContent').controls;let stageIndex = index">
<input nz-input [formControlName]="stageIndex.toString()">
</nz-row>
</nz-form-control>
</nz-row>
</div>
</div>
</nz-form-control>
第二种:将stageName(FormControl)、stageType(FormControl)、stageContent(FormArray)这些组成的 FormGroup 的信息分别写在对应的FormControl的外面。
<nz-form-control nzSpan="18">
<div class="workFlowContent" formArrayName="workFlowContent"
*ngFor="let content of validateForm.controls['workFlowContent'].controls;
let workflowIndex = index">
<nz-row>
<nz-form-label nzSpan="3" nz-col>
stage{{workflowIndex + 1}}
</nz-form-label>
</nz-row>
<nz-row>
<nz-form-label nzSpan="3" nz-col>stageName</nz-form-label>
<nz-form-control nz-col nzSpan="7" [formGroupName]="workflowIndex.toString()">
<input nz-input type="text" formControlName="stageName">
</nz-form-control>
<nz-form-label nzOffset="1" nzSpan="3" nz-col>stageType
</nz-form-label>
<nz-form-control nz-col nzSpan="7" [formGroupName]="workflowIndex.toString()">
<input nz-input type="text" formControlName="stageType">
</nz-form-control>
</nz-row>
<nz-row>
<nz-form-label nzSpan="3" nz-col>stageContent</nz-form-label>
<nz-form-control nz-col nzSpan="18" [formGroupName]="workflowIndex.toString()">
<nz-row formArrayName="stageContent"
*ngFor="let stage of content.get('stageContent').controls;let stageIndex = index">
<input nz-input [formControlName]="stageIndex.toString()">
</nz-row>
</nz-form-control>
</nz-row>
</div>
</nz-form-control>
增加一点点样式,以突出显示stage信息
.workFlowContent {
background: #e6f7ff;
border: 1px solid #91d5ff;
padding: 10px;
border-radius: 4px;
}
保存运行以后,就会发现,原来的workFlowContent数组中的元素由一个单纯的’string’ 类型办成了 ‘object’ 类型,并且这个object中的key都是FormGroup中的每一个实例。
- 添加两个按钮,使FormArray:workFlowContent 可以动态的增减。
为了方便,将 workFlowContent 使用get 将其装换成FormArray;
get workFlowContent() {
return this.validateForm.get('workFlowContent') as FormArray;
}
然后修改模板文件,使用属性workFlowContent;
将原来的
<div class="workFlowContent" formArrayName="workFlowContent"
*ngFor="let content of validateForm.controls['workFlowContent'].controls;
let workflowIndex = index">
...
...
</div>
修改为:
<div class="workFlowContent" formArrayName="workFlowContent"
*ngFor="let content of workFlowContent.controls;
let workflowIndex = index">
...
...
</div>
a. 添加 ‘增加stage’ 的按钮,并绑定一个点击事件
模板文件:
<button nz-button (click)="addStage()">增加stage</button>
类文件:
public addStage(): void {
this.workFlowContent.push(
this.fb.group({
stageName: [null, [Validators.required]],
stageType: [null, [Validators.required]],
stageContent: this.fb.array([
this.fb.control(null)
]),
})
);
}
保存运行:
b. 添加 ‘删除’ 的按钮,并绑定一个点击事件
模板文件:
<nz-row>
<nz-form-label nzSpan="3" nz-col>
stage{{workflowIndex + 1}}
</nz-form-label>
<nz-form-control nzOffset="15" nzSpan="3" nz-col>
<button nz-button (click)="removeStage(workflowIndex)">
删除
</button>
</nz-form-control>
</nz-row>
类文件:
public removeStage(workflowIndex: number): void {
this.workFlowContent.removeAt(workflowIndex);
}
保存运行:
构造出stage内容(stageContent)中的的实例
将 workFlowContent 中的每一个 stageContent 实例中的this.fb.control(null),转换成this.fb.group({})。
在这个this.fb.group({})中添加workFlowContent(FormGroup的实例).
- 修改workFlowContent(FormGroup的实例):
stageContent: this.fb.array([
this.fb.group({
stepName: [null, [Validators.required]],
stepType: [null, [Validators.required]],
stepContent: this.fb.array([
this.fb.control(null)
])
})
]),
现在以:stepName(FormControl)、stepType(FormControl)、stepContent(FormArray)这三个为一个FormGroup作为 stageContent 的其中一个实例。
由于stageContent是workFlowContent中的实例,所以在修了初始化的部分,还需要修改增加按钮的点击事件
public addWorkflow(): void {
this.workFlowContent.push(
this.fb.group({
stageName: [null, [Validators.required]],
stageType: [null, [Validators.required]],
stageContent: this.fb.array([
this.fb.group({
stepName: [null, [Validators.required]],
stepType: [null, [Validators.required]],
stepContent: this.fb.array([
this.fb.control(null)
])
})
]),
})
);
}
- 修改模板文件
<nz-row>
<nz-row>
<nz-form-label nzSpan="3" nz-col>
stageContent
</nz-form-label>
</nz-row>
<nz-row>
<nz-form-control nzOffset="3" nzSpan="18" [formGroupName]="workflowIndex.toString()">
<div formArrayName="stageContent"
class="stageContent"
*ngFor="let stage of content.get('stageContent').controls;let stageIndex = index">
<div [formGroupName]="stageIndex.toString()">
<nz-row>
<nz-form-label nzSpan="3" nz-col>step{{stageIndex + 1}}</nz-form-label>
</nz-row>
<nz-row>
<nz-form-label nzSpan="3" nz-col>stepName</nz-form-label>
<nz-form-control nzSpan="7" nz-col>
<input nz-input type="text" formControlName="stepName" placeholder="请输入stepName">
</nz-form-control>
<nz-form-label nzOffset="1" nzSpan="3" nz-col>stepType</nz-form-label>
<nz-form-control nzSpan="7" nz-col>
<input nz-input type="text" formControlName="stepType" placeholder="请输入stepType">
</nz-form-control>
</nz-row>
<nz-row>
<nz-form-label nzSpan="3" nz-col>stepContent</nz-form-label>
<nz-form-control nzSpan="7" nz-col formArrayName="stepContent">
<nz-row *ngFor="let step of stage.get('stepContent').controls;let stepIndex = index">
<input nz-input type="text"
[formControlName]="stepIndex.toString()"
placeholder="请输入stepContent">
</nz-row>
</nz-form-control>
</nz-row>
</div>
</div>
</nz-form-control>
</nz-row>
</nz-row>
为了方便区别,在step增加一些样式:
.stageContent {
background: #fffbe6;
border: 1px solid #ffe58f;
padding: 10px;
border-radius: 4px;
margin-bottom: 10px;
}
运行保存
- 添加两个按钮,使FormArray:stageContent 可以动态的增减。
a. 添加 ‘增加step’ 按钮
修改模板文件
<button nz-button (click)="addStep(workflowIndex)">增加step</button>
在类文件中增加 addStep() 方法
// add stage
public addStep(workflowIndex: number): void {
(this.workFlowContent.at(workflowIndex).get('stageContent') as FormArray).push(
this.fb.group({
stepName: [null, [Validators.required]],
stepType: [null, [Validators.required]],
stepContent: this.fb.array([
this.fb.control(null)
])
}));
}
b. 添加 ‘删除step’ 按钮
修改模板文件
<nz-row>
<nz-form-label nzSpan="3" nz-col>step{{stageIndex + 1}}</nz-form-label>
<nz-form-control nzOffset="15" nzSpan="3">
<button nz-button (click)="removeStep(workflowIndex,stageIndex)">删除step</button>
</nz-form-control>
</nz-row>
在类文件中增加 removeStep() 方法
removeStep(workflowIndex: number, stageIndex: number) {
(this.workFlowContent.at(workflowIndex).get('stageContent') as FormArray).removeAt(stageIndex);
}
在这种情况下,是将取到workFlowContent 中第 workflowIndex 个 FormGroup,在这个FormGroup的stageContent实例中中找到第stageIndex个stageContent中的FormGroup,然后将其删除。
为了区别stage和step的删除,将原来删除stage 的按钮修改为’删除stage’,
保存运行以后
- 添加两个按钮,使FormArray:stepeContent 可以动态的增减。
在这里使用一种简单的方式,不在关注index,而是直接使用遍历出来的元素
a. 添加 ‘增加stepContent’ 按钮
修改模板文件
<nz-row>
<button nz-button (click)="addStepContent(stage)">
添加StepContent
</button>
</nz-row>
由于StepContent 是 stage 实例中的,所以只要将 stage的 stepContent as FormArray,然后在其中增加
在类文件中增加 addStepContent() 方法
// add StepContent
public addStepContent(stage: FormGroup) {
(stage.get('stepContent') as FormArray).push(this.fb.control(null));
}
b. 添加 ‘删除step’ 按钮(采用和增加StepContent一样的思路进行删除操作)
修改模板文件
<nz-form-control nzSpan="4">
<button nz-button (click)="removeStepContent(stage,stepIndex)">
删除StepContent
</button>
</nz-form-control>
在类文件中增加 removeStep() 方法
// remove StepContent
public removeStepContent(stage: FormGroup, stepIndex: number): void {
(stage.get('stepContent') as FormArray).removeAt(stepIndex);
}
保存运行:
这样深层嵌套的表单就完成了。
代码已上传GitHub中的form-nested组件。