在Angular响应式表单的开发场景中,动态增减表单项是非常常见的需求,比如需要批量录入多个联系人的信息、动态添加多个商品规格配置等,这类场景下使用FormArray可以高效完成动态数据的管理。

FormArray基础概念
FormArray是Angular响应式表单中的三大核心表单控件之一,另外两个是FormControl和FormGroup。FormArray用于存储一组有序的表单控件,这些控件可以是FormControl、FormGroup甚至是嵌套的FormArray,非常适合处理数量不固定的动态数据集合。
和FormGroup不同,FormArray不需要为每个子控件指定固定的键名,而是通过索引来访问和操作内部的控件,这让动态增减控件变得非常灵活。
创建FormArray的方式
1. 初始化空FormArray
如果一开始不需要默认的动态项,可以创建一个空的FormArray,后续再通过方法动态添加控件。
import { Component, OnInit } from '@angular/core';
import { FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms';
@Component({
selector: 'app-dynamic-form',
templateUrl: './dynamic-form.component.html',
styleUrls: ['./dynamic-form.component.css']
})
export class DynamicFormComponent implements OnInit {
// 使用FormBuilder简化表单创建
constructor(private fb: FormBuilder) {}
// 定义响应式表单
dynamicForm: FormGroup = this.fb.group({
// 创建一个空的FormArray,用于存放动态的联系人项
contacts: this.fb.array([])
});
ngOnInit(): void {
// 初始化时可以添加默认项
this.addContact();
}
// 获取contacts对应的FormArray实例
get contactsArray(): FormArray {
return this.dynamicForm.get('contacts') as FormArray;
}
}
2. 初始化带默认值的FormArray
如果页面加载时需要展示默认的表单项,可以在创建FormArray时传入初始的控件数组。
// 初始化时带两个默认联系人项
dynamicForm: FormGroup = this.fb.group({
contacts: this.fb.array([
this.fb.group({
name: ['张三', Validators.required],
phone: ['13800138000', [Validators.required, Validators.pattern(/^1[3-9]d{9}$/)]]
}),
this.fb.group({
name: ['李四', Validators.required],
phone: ['13900139000', [Validators.required, Validators.pattern(/^1[3-9]d{9}$/)]]
})
])
});
FormArray常用操作方法
添加动态项
使用FormArray的push方法可以向数组末尾添加新的表单控件,通常我们会封装一个专门的方法来添加项。
// 添加一个新的联系人表单项
addContact(): void {
const contactGroup = this.fb.group({
name: ['', Validators.required],
phone: ['', [Validators.required, Validators.pattern(/^1[3-9]d{9}$/)]]
});
// 将新的表单组添加到contacts数组中
this.contactsArray.push(contactGroup);
}
删除动态项
使用FormArray的removeAt方法可以删除指定索引位置的控件,需要传入要删除的项的索引值。
// 删除指定索引的联系人项
removeContact(index: number): void {
this.contactsArray.removeAt(index);
}
获取FormArray的值
可以通过FormArray的value属性直接获取所有动态项的当前值,也可以通过getRawValue方法获取包含禁用控件的值。
// 获取所有联系人的值
getAllContactsValue(): void {
const contactsValue = this.contactsArray.value;
console.log('当前所有联系人数据:', contactsValue);
}
模板中渲染动态表单
在HTML模板中,我们需要遍历FormArray来渲染每一个动态表单项,同时绑定对应的表单控件。
<form [formGroup]="dynamicForm">
<div formArrayName="contacts">
<!-- 遍历contacts数组,生成每一个联系人项 -->
<div *ngFor="let contact of contactsArray.controls; let i = index" [formGroupName]="i">
<h3>联系人 {{ i + 1 }}</h3>
<div>
<label>姓名:</label>
<input formControlName="name" placeholder="请输入姓名" />
<!-- 展示姓名校验错误 -->
<div *ngIf="contact.get('name')?.invalid && contact.get('name')?.touched">
姓名不能为空
</div>
</div>
<div>
<label>电话:</label>
<input formControlName="phone" placeholder="请输入手机号" />
<!-- 展示电话校验错误 -->
<div *ngIf="contact.get('phone')?.invalid && contact.get('phone')?.touched">
请输入正确的手机号
</div>
</div>
<button type="button" (click)="removeContact(i)">删除该联系人</button>
</div>
</div>
<button type="button" (click)="addContact()">添加联系人</button>
<button type="button" (click)="getAllContactsValue()">获取所有数据</button>
</form>
FormArray使用注意事项
- 操作FormArray时,一定要通过get方法获取对应的FormArray实例,不要直接操作原表单属性,避免类型错误。
- 如果动态项中包含复杂的嵌套结构,可以在FormArray中嵌套FormGroup,每个FormGroup对应一个完整的动态项数据结构。
- 动态增减项之后,Angular会自动更新表单的状态和值,不需要手动触发变更检测。
- 如果需要清空整个FormArray,可以使用clear方法,它会移除数组中的所有控件。
// 清空所有联系人项
clearAllContacts(): void {
this.contactsArray.clear();
}
实际业务场景示例
假设我们需要开发一个商品规格配置页面,每个商品可以有多个规格,每个规格包含规格名和规格值,使用FormArray的实现方式如下:
export class GoodsSpecComponent implements OnInit {
specForm: FormGroup = this.fb.group({
goodsName: ['', Validators.required],
specs: this.fb.array([])
});
get specsArray(): FormArray {
return this.specForm.get('specs') as FormArray;
}
// 添加规格项
addSpec(): void {
const specGroup = this.fb.group({
specName: ['', Validators.required],
specValue: ['', Validators.required]
});
this.specsArray.push(specGroup);
}
// 删除规格项
removeSpec(index: number): void {
this.specsArray.removeAt(index);
}
ngOnInit(): void {
// 初始化添加一个默认规格项
this.addSpec();
}
}
通过FormArray的灵活运用,我们可以轻松应对各种动态数据的表单管理需求,相比手动操作DOM或者其他方式,响应式表单的FormArray在状态管理、数据校验、值获取等方面都有天然的优势,能大幅减少开发工作量,提升代码的可维护性。