Angular FormArray中如何根据其他FormControl变化更新关联字段值
解决方案
一、核心修改逻辑
- 每个FormGroup创建时就订阅carbohydrates、fats、protein三个字段的valueChanges,自动计算更新当前行的calories值
- 修正FormArray字段访问方式,不需要单独维护错误的
getCalories方法 - 同步FormArray和独立foods数组的数值,避免数据不一致
- 卡路里展示标签不需要绑定formControl,直接读取数值即可
二、修改前注意事项
需先导入rxjs相关依赖:
import { combineLatest, Subscription } from 'rxjs'; import { startWith } from 'rxjs/operators'; import { OnDestroy } from '@angular/core';
三、修改后的TS代码
export class FoodDetailsComponent implements OnInit, OnDestroy { items!: FormArray foods:any = FOODDATAS; // 存储订阅用于销毁,避免内存泄漏 private subscriptions = new Subscription(); constructor(private fb : FormBuilder) {} ngOnInit(): void { const toGroup = this.foods.map((food:any, index: number) => { const group = new FormGroup({ name: new FormControl(food.name,[ Validators.required, Validators.pattern(/^[a-zA-Z]*$/) ]), carbohydrates: new FormControl(food.carbohydrates, [ Validators.required, Validators.pattern(/^\d+(\.\d{1,2})?$/) ]), fats: new FormControl(food.fats, [ Validators.required, Validators.pattern(/^\d+(\.\d{1,2})?$/) ]), protein: new FormControl(food.protein, [ Validators.required, Validators.pattern(/^\d+(\.\d{1,2})?$/) ]), calories: new FormControl(food.calories, [ Validators.required ]) }); // 订阅当前行三个营养字段的变化,自动计算卡路里 const calSub = combineLatest([ group.get('carbohydrates')!.valueChanges.pipe(startWith(group.get('carbohydrates')!.value)), group.get('fats')!.valueChanges.pipe(startWith(group.get('fats')!.value)), group.get('protein')!.valueChanges.pipe(startWith(group.get('protein')!.value)) ]).subscribe(([carbs, fats, protein]) => { // 数值合法才计算 if (!isNaN(Number(carbs)) && !isNaN(Number(fats)) && !isNaN(Number(protein))) { const calories = Number(carbs) * 4 + Number(protein) * 4 + Number(fats) * 9; // 更新FormArray中的calories值,不触发事件避免循环监听 group.get('calories')?.setValue(calories, { emitEvent: false }); // 同步更新foods数组,保证展示一致 this.foods[index].calories = calories; } }); this.subscriptions.add(calSub); return group; }); this.items = new FormArray(toGroup) } getControl(index: number, field: string): FormControl { return this.items.at(index).get(field) as FormControl; } updateField(index: number, field: string) { const control = this.getControl(index, field); if (control.valid) { this.foods = this.foods.map((e: any, i: number) => { if (index === i) { return { ...e, [field]: control.value }; } return e; }); } } ngOnDestroy(): void { // 销毁所有订阅 this.subscriptions.unsubscribe(); } }
四、修改后的HTML代码(仅卡路里部分)
<td> <div> <p> {{food.calories | number:'1.0-2'}} kcals </p> </div> </td>
五、FormArray字段访问规范
要获取FormArray中指定行的指定字段值,固定写法如下:
// index为行索引,fieldName为字段名 const value = this.items.at(index).get(fieldName)?.value;
遍历所有行取值写法:
this.items.controls.forEach((group, index) => { const carbs = group.get('carbohydrates')?.value; // 其他业务逻辑 })
内容的提问来源于stack exchange,提问作者user16605035
相关产品推荐
相关产品推荐

