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

Angular FormGroup patchValue/setValue未触发属性装饰器getter问题

问题:自定义@Amount装饰器修饰的属性无法被Angular表单patchValue读取

我尝试使用自定义属性装饰器@Amount()将属性值格式化为特定格式,但发现Angular表单的patchValue方法无法读取该装饰器修饰的属性值,相关代码如下:

amount.decorator.ts

export const Amount = () => 
  (target: any, key: string) => {
    let _value: number | string = target[key];

    if (delete target[key]) {
      return Object.defineProperty(target, key, {
        configurable: false,
        enumerable: true,
        get: () => {
          console.log("returning", "$" + _value);
          return "$" + _value;
        },
        set: (val) => {
          console.log("setting val", val);
          _value = val;
        },
      });
    }
  }

summary.model.ts

export class Summary {
  regularField!: string;
  @Amount() amountField!: number | string;
} 

form.component.ts

export class SummaryComponent implements OnInit {
  summary: Summary;
  form: FormGroup = new FormGroup({
    regularField: new FormControl(''),
    amountField: new FormControl('Initial value'),
  });

  ngOnInit() {
    this.summary = this.service.getSummary();
    // summary.regularField 为 "Hello world!"
    // summary.amountField 为 123.00
    
    this.summary.amountField = 456.00; <-- 如预期触发"setting val"日志

    console.log(this.summary.amountField); <-- 如预期触发@Amount()的"returning"日志

    console.log(this.form.controls['amountField'].value); <-- 显示"Initial value"

    this.form.patchValue(this.summary || {}); <-- 预期会调用getter来更新表单控件,但未触发

    console.log(this.form.controls['amountField'].value); <-- 仍显示"Initial value"
  }
}

原因分析

Angular表单的patchValue方法在处理传入对象时,只会遍历对象自身的可枚举属性(类似Object.keys()的行为)。而你的@Amount()装饰器是作用在Summary类的原型对象上的,当创建Summary实例后,amountField是原型链上的访问器属性,并非实例自身的属性,因此patchValue无法识别到这个属性,自然不会调用其getter来获取值。

解决方案

你可以通过以下几种方式解决这个问题:

方案1:手动构造包含目标属性的对象传入patchValue

直接明确指定需要赋值的属性,主动调用getter获取格式化后的值:

this.form.patchValue({
  regularField: this.summary.regularField,
  amountField: this.summary.amountField
});

方案2:在Summary类中实现toJSON方法

通过自定义toJSON方法,返回包含所有需要属性的普通对象,这样patchValue就能正确读取到值:

export class Summary {
  regularField!: string;
  @Amount() amountField!: number | string;

  toJSON() {
    return {
      regularField: this.regularField,
      amountField: this.amountField
    };
  }
}

然后调用时:

this.form.patchValue(this.summary.toJSON() || {});

方案3:修改装饰器,在实例上定义访问器属性

如果希望装饰器自动处理实例属性,可以调整装饰器逻辑,在实例创建时为其添加访问器:

export const Amount = () => 
  (target: any, key: string) => {
    // 用符号属性存储实例私有值,避免命名冲突
    const storageKey = Symbol(key);
    // 保存原构造函数
    const originalConstructor = target.constructor;

    // 重写构造函数,在实例初始化时添加访问器
    target.constructor = function(...args: any[]) {
      const instance = new originalConstructor(...args);
      // 初始化实例的私有存储值
      instance[storageKey] = instance[key] ?? '';
      // 在实例自身定义访问器
      Object.defineProperty(instance, key, {
        configurable: false,
        enumerable: true,
        get: () => {
          console.log("returning", "$" + instance[storageKey]);
          return "$" + instance[storageKey];
        },
        set: (val) => {
          console.log("setting val", val);
          instance[storageKey] = val;
        },
      });
      return instance;
    };
  }

内容的提问来源于stack exchange,提问作者djmistral

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 13:35:40