You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 21:24:03