angular10 FormArray 实现动态增减表单项
场景一,动态增减项只有一个表单元素的时候:
场景二:动态增减项为多个元素:
先创建一个响应式表单
form.components.ts
// 注意:此处只贴出部分相关代码
import { FormGroup, FormBuilder, FormArray, FormControl } from '@angular/forms';
export class FormComponent implements OnInit{
public form: FormGroup;
constructor(
private fb: FormBuilder,
) {
this.createForm();
}
ngOnInit(): void {
}
private createForm() {
this.form = this.fb.group({
// 针对场景一定义一个子控件,类型为 FormArray
strike_schedules: this.fb.array([]),
// 针对场景二定义一个子控件,类型为 FormArray
instrument_ids: this.fb.array([]),
});
// 初始化时增加一项
this.addStrikeScheduleItem();
this.addInstrumentIdItem();
}
/**
* 往 strike_schedules FormArray 中 push 一个 FormControl
*/
private newStrikeScheduleItem(): FormControl {
return this.fb.control(null);
}
get strikeSchedules(): FormArray {
return this.form.get('strike_schedules') as FormArray;
}
public addStrikeScheduleItem() {
this.strikeSchedules.push(this.newStrikeScheduleItem());
}
public removeStrikeScheduleItem(index: number) {
this.strikeSchedules.removeAt(index);
}
/**
* 往 instrument_ids FormArray 中 push 一个 FormGroup
*/
private newInstrumentIdItem(): FormGroup {
return this.fb.group({
code: [null, Validators.required],
scheme: [null, Validators.required],
});
}
get instrumentIds(): FormArray {
return this.form.get('instrument_ids') as FormArray;
}
public addInstrumentIdItem() {
this.instrumentIds.push(this.newInstrumentIdItem());
}
public removeInstrumentIdItem(index: number) {
this.instrumentIds.removeAt(index);
}
}
form.components.html
<form nz-form [formGroup]="form" novalidate class="tiny-form">
<!-- 动态增减 strike schedule -->
<div formArrayName="strike_schedules" class="p-10 pr-15" >
<div nz-row nzJustify="space-between">
<h4 nz-col class="text-16 text-bold">strike schedule</h4>
<button nz-col nz-button nzType="primary" nzGhost nzSize="small" (click)="addStrikeScheduleItem()">
<i nz-icon nzType="plus" nzTheme="outline"></i>
</button>
</div>
<nz-form-item nz-row nzJustify="space-between"
*ngFor="let item of strikeSchedules.controls; index as scheduleIndex">
<nz-form-control nz-col nzSpan="24">
<nz-date-picker nzSize="small" class="schedule-date" [nzFormat]="showDateFormatter"
[formControl]="strikeSchedules.controls[scheduleIndex]">
</nz-date-picker>
<button nz-button nzType="primary" nzDanger nzGhost nzSize="small"
(click)="removeStrikeScheduleItem(scheduleIndex)">
<i nz-icon nzType="minus" nzTheme="outline"></i>
</button>
</nz-form-control>
</nz-form-item>
</div>
<!-- 动态增减 instrument ids -->
<div formArrayName="instrument_ids" class="mb-5 mt-10">
<ng-container *ngFor="let item of instrumentIds.controls; index as i">
<nz-form-item [formGroupName]="i" class="mt-5">
<nz-form-label nzSpan="10" nzRequired>
<ng-container *ngIf="i === 0">instrument ids</ng-container>
</nz-form-label>
<nz-form-control nzSpan="14">
<nz-input-group nzSize="small">
<div nz-row>
<div nz-col nzSpan="10">
<nz-select formControlName="scheme" [nzShowSearch]="true" nzSize="small"
class="full-width">
<nz-option *ngFor="let item of metaList?.instrument_id_schema_bond"
[nzLabel]="item" [nzValue]="item">
</nz-option>
</nz-select>
</div>
<div nz-col nzSpan="14" class="pl-10 flex-space-between">
<input type="text" nz-input formControlName="code" />
<div class="pl-5 text-right" *ngIf="!ifDetail">
<button *ngIf="i === 0" nz-button nzType="primary" nzGhost nzSize="small"
(click)="addInstrumentIdItem()">
<i nz-icon nzType="plus" nzTheme="outline"></i>
</button>
<button *ngIf="i !== 0" nz-button nzType="primary" nzDanger nzGhost
nzSize="small" (click)="removeInstrumentIdItem(i)">
<i nz-icon nzType="minus" nzTheme="outline"></i>
</button>
</div>
</div>
</div>
</nz-input-group>
</nz-form-control>
</nz-form-item>
</ng-container>
</div>
</form>
formArray初始化赋值
// 注意:此处 detailInfo.strike_schedules = ['2021-10-25', ...]
// detailInfo.instrument_ids = [{code: '', scheme: ''}, ...]
private initForm(detailInfo) {
// strike_schedules
if (detailInfo.strike_schedules.length) {
this.strikeSchedules.clear();
structuredDetail.strike_schedules.map((item, index, arr) => {
this.strikeSchedules.push(this.newStrikeScheduleItem());
});
}
// instrument_ids
if (detailInfo.instrument_ids.length) {
this.instrumentIds.clear();
structuredDetail.instrument_ids.map((item, index, arr) => {
this.instrumentIds.push(this.newInstrumentIdItem());
});
}
this.form.patchValue(detailInfo);
}
formArray 表单校验状态提示
public submit() {
if (this.form.valid) {
this.httpRequest();
} else {
this.formMarkAsDirtyCallback(this.form);
this.messageService.error('the form is invalid');
}
}
/**
* 触发表单校验状态
*/
private formMarkAsDirtyCallback(form) {
for (const key in form.controls) {
if (form.controls.hasOwnProperty(key)) {
form.controls[key].markAsDirty();
form.controls[key].updateValueAndValidity();
// 如果 controls.key 里面还有 controls,则子控件类型可能为 FormGroup 或者 FormArray
if (form.controls[key].hasOwnProperty('controls')) {
if (form.controls[key]['length']) {
// 如果 controls.key.length 不是 undefined,则该控件为 FormArray
const formArr = form.get(key) as FormArray;
for (const formArrIndex in formArr.controls) {
if (formArr.controls.hasOwnProperty(formArrIndex)) {
// 标记 FormArray 里面的 control
const element = formArr.controls[formArrIndex] as FormGroup;
this.formMarkAsDirtyCallback(element);
}
}
} else {
// 如果没有 controls.key.length 属性,则是 FormGroup 类型,此时重新循环校验
this.formMarkAsDirtyCallback(form.controls[key]);
}
}
}
}
}