实现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
相关产品推荐
相关产品推荐

