Angular13:将含嵌套对象与数组的JSON模型填充至嵌套组件响应式FormGroup
解决Angular响应式表单嵌套数组与模型匹配问题
核心问题原因
- Angular
FormBuilder.group()接收普通对象时,会把数组类型的属性直接转为FormControl,而非FormArray patchValue()只会更新已有表单控件的值,不会自动把数组数据转换为FormArray的子控件,反而会覆盖FormArray为FormControl
解决方案步骤
1. 编写递归创建表单结构的辅助函数
根据模型的嵌套层级,递归生成对应的 FormGroup 和 FormArray,不能直接用模型实例初始化表单:
// AppComponent内添加辅助方法 private createCustomerHistoryForm(customer: CustomerHistory): FormGroup { return this.fb.group({ id: [customer.id], orders: this.fb.array(customer.orders.map(order => this.createOrderForm(order))) }); } private createOrderForm(order: Order): FormGroup { return this.fb.group({ id: [order.id], orderItems: this.fb.array(order.orderItems.map(item => this.createOrderItemForm(item))) }); } private createOrderItemForm(item: OrderItem): FormGroup { return this.fb.group({ id: [item.id], itemName: [item.itemName] }); }
2. 初始化表单(空数据或带初始数据)
如果是初始化空表单,基于空模型实例创建;如果已有后端返回数据,直接传入生成表单:
initFormGroup() { // 初始化空表单 const emptyCustomer = new CustomerHistory(); this.myCustomerHistoryFormGroup = this.createCustomerHistoryForm(emptyCustomer); // 若直接用后端数据初始化,替换为: // this.myCustomerHistoryFormGroup = this.createCustomerHistoryForm(后端返回的customerHistory); }
3. 正确实现patchValue(处理嵌套数组)
不能直接全量调用patchValue,需单独处理数组部分:先清空现有FormArray,再逐个添加对应FormGroup并填充数据:
patchFormGroup(customerHistory: CustomerHistory) { // 更新非数组字段 this.myCustomerHistoryFormGroup.patchValue({ id: customerHistory.id }); // 处理orders数组 const ordersArray = this.myCustomerHistoryFormGroup.get('orders') as FormArray; ordersArray.clear(); // 清空原有控件 // 遍历后端数据,生成对应的Order表单并添加到数组 customerHistory.orders.forEach(order => { const orderForm = this.createOrderForm(order); ordersArray.push(orderForm); }); }
4. 添加便捷方法获取FormArray(简化模板代码)
在组件中添加获取数组的快捷方法,模板里调用更简洁:
get ordersFormArray(): FormArray { return this.myCustomerHistoryFormGroup.get('orders') as FormArray; }
5. 模板中正确遍历FormArray
修改模板,使用便捷方法遍历,并正确传递FormGroup给子组件:
<form [formGroup]="myCustomerHistoryFormGroup"> <div formArrayName='orders'> <div *ngFor="let order of ordersFormArray.controls; let i=index" [formGroupName]="i"> <app-order [formGroup]="ordersFormArray.at(i)"></app-order> </div> </div> </form>
6. 嵌套Order组件的处理
Order组件内部同样需要处理orderItems的FormArray,逻辑和父组件一致:
// order.component.ts import { Component, Input } from '@angular/core'; import { FormGroup, FormArray, FormBuilder } from '@angular/forms'; @Component({ selector: 'app-order', templateUrl: './order.component.html' }) export class OrderComponent { @Input() formGroup!: FormGroup; constructor(private fb: FormBuilder) {} get orderItemsFormArray(): FormArray { return this.formGroup.get('orderItems') as FormArray; } // 示例:添加新的订单项 addOrderItem() { const emptyItemForm = this.fb.group({ id: null, itemName: '' }); this.orderItemsFormArray.push(emptyItemForm); } }
Order组件模板示例:
<div [formGroup]="formGroup"> <p>订单ID: <input formControlName="id"></p> <div formArrayName="orderItems"> <div *ngFor="let item of orderItemsFormArray.controls; let i=index" [formGroupName]="i"> <p>商品名称: <input formControlName="itemName"></p> </div> <button (click)="addOrderItem()">添加商品</button> </div> </div>
内容的提问来源于stack exchange,提问作者Holger Rattenscheid
相关产品推荐
相关产品推荐

