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

实现ControlValueAccessor的可复用组件:程序化赋值时onChange触发两次

问题解决方案

核心问题是手动输入的ionChange绑定与程序化设置时的onChange调用重复触发,以下是修正后的实现方案:

1. 修改组件类代码

将组件内部的value改为私有变量+getter/setter封装,统一通过setter触发父表单的onChange,避免重复通知:

export class PhoneNumberFormControl implements ControlValueAccessor {

  private _value: string;
  get value(): string {
    return this._value;
  }
  set value(newVal: string) {
    if (newVal !== this._value) {
      this._value = newVal;
      this.onChange(newVal); // 统一触发父表单值变更通知
    }
  }

  onChange = (newValue: string) => {};
  onTouched = () => {};

  constructor(
    @Optional() @Self() public ngControl: NgControl
  ) {
    if (ngControl != null) {
      ngControl.valueAccessor = this;
    }
  }

  writeValue(value: string): void {
    // 父表单传值时直接修改私有变量,避免触发setter中的onChange(父表单已知道值变化)
    if (value !== this._value) {
      this._value = value;
    }
  }

  registerOnChange(fn: any): void {
    this.onChange = fn;
  }

  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }

  setDisabledState(isDisabled: boolean): void {
    // 可根据需求实现禁用逻辑,比如禁用ion-input
  }

  async openModal() {
    // 打开模态框逻辑
    const phoneNumber = '7871234567';

    if (phoneNumber) {
      this.value = phoneNumber; // 直接赋值,通过setter触发一次通知
      this.onTouched(); // 标记输入已触碰,逻辑正确
    }
  }
}

2. 修改模板代码

移除ion-input上的(ionChange)绑定,因为现在统一通过value的setter触发通知:

<ion-input [(ngModel)]="value"
           (ionBlur)="onTouched()">
</ion-input>

<ion-button (click)="openModal()">
  从通讯录选择
</ion-button>

修正逻辑说明

  • 手动输入场景:ngModel更新value时,触发setter中的onChange,仅通知父表单一次。
  • 通讯录选择场景:直接赋值this.value,通过setter触发onChange,同时ngModel同步更新ion-input的值,但因为模板已移除ionChange绑定,不会重复触发通知。
  • 父表单传值场景:writeValue直接修改私有变量,避免触发onChange(父表单本身就是值变更的发起者,无需重复通知)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 17:36:16