使用*ngFor遍历FormArray时删除元素触发错误
Angular FormArray 删除商品时出现 undefined 错误的解决
我在《Learning Angular No Nonsense》书中的FormArray示例基础上,做了购物车动态表单的改进,添加删除商品功能时触发了错误。删除商品时,模板里的{{ cart2[i].name }}和(click)="removeProduct(i)"行抛出错误:ERROR TypeError: ctx_r1.cart2[i_r4] is undefined。
问题代码
Cart.component.html
<form [formGroup]="cartForm2" (ngSubmit)="validateCart()"> <div formArrayName="products" *ngFor="let product of cartForm2.controls.products.controls; let i = index" > <label>{{ cart2[i].name }} : </label> <input type="number" [formControlName]="i" /> <button type="button" (click)="removeProduct(i)">Remove</button> <span *ngIf="product.touched && product.hasError('required')"> 此字段为必填项</span > <span *ngIf="product.touched && product.hasError('min')"> 数值不能小于1</span > </div> <p>cartForm.value : {{ cartForm2.value | json }}</p> <div> <button type="submit" [disabled]="!cartForm2.valid">确认</button> </div> </form>
cart.component.ts
cartForm2 = new FormGroup({ products: new FormArray<FormControl<number>>([]), }); cart2: Product[] = []; ngOnInit(): void { this.cart2 = this.cartService.cart; this.cart2.forEach(() => { this.cartForm2.controls.products.push( new FormControl(1, { nonNullable: true, validators: [Validators.required, Validators.min(1)], }) ); }); } removeProduct(index: number) { this.cartService.deleteProduct(index); this.cart2 = this.cartService.cart this.cartForm2.controls.products.removeAt(index); console.log(JSON.stringify(this.cart2)); }
cart.service.ts
cart: Product[] = []; constructor() {} addProduct(product: Product) { this.cart.push(product); } deleteProduct(index: number) { this.cart.splice(index, 1); }
错误原因
模板遍历的是cartForm2.controls.products.controls,但获取商品名称时依赖外部的cart2[i]。删除操作时,先更新cart2再移除FormArray中的控件,Angular变更检测会出现短暂不同步:此时FormArray的控件数量还未减少,而cart2已经少了一个元素,导致遍历到的索引i在cart2中找不到对应元素,抛出undefined错误。
解决方法
改用FormGroup封装每个商品的信息(名称和数量),让FormArray的每个元素都包含商品名称的FormControl,直接从FormControl中获取名称,不再依赖外部的cart2数组:
修改后的代码
cart.component.ts 部分修改
this.cart2.forEach((product) => { this.cartForm2.controls.products.push( new FormGroup({ name: new FormControl(product.name), quantity: new FormControl(1, { nonNullable: true, validators: [Validators.required, Validators.min(1)], }), }) ); });
Cart.component.html 部分修改
<label>{{ product.controls["name"].value }}</label>
修改后,模板直接从当前遍历的FormGroup中获取商品名称,避免了索引不同步的问题,删除操作时不再出现undefined错误。
内容的提问来源于stack exchange,提问作者v3geto
相关产品推荐
相关产品推荐

