考虑以下反应形式设置:
让我们拥有实现ControlValueAccessor
接口的自定义SimpleInputComponent
。将它添加到用ngIf
指令包装的应用程序模板中,这样我们就可以在ngIf
表达式更改时重新调用SimpleInputComponent
的生命周期。
由于SimpleInputComponent
通过formControlName
指令绑定到FormControl
实例,因此可以通过调用FormControl.setValue
方法来更改其值。
我期望FormControl.setValue
正好触发SimpleInputComponent.writeValue
一次。不幸的是,事实并非如此。
FormControl.setValue
调用SimpleInputComponent.writeValue
的次数与调用SimpleInputComponent
的生命周期的次数一样多。
现在要模拟这个问题,你至少需要两个组件:
SimpleInputComponent:
import { Component, forwardRef, Input } from '@angular/core';
import { NG_VALUE_ACCESSOR, ControlValueAccessor, FormControlName } from '@angular/forms';
@Component({
selector: 'simple-input-text',
template: `
<input [name]="formControlName" type="text"
[(ngModel)]="value"
(input)="onChange($event)"
(blur)="onBlur()" />
`,
providers: [
{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => SimpleInputComponent), multi: true }
]
})
export class SimpleInputComponent implements ControlValueAccessor {
private value: string;
constructor() { }
propagateChange: any = () => {};
propagateTouch: any = () => {};
onBlur() {
this.propagateTouch();
}
onChange(event) {
this.propagateChange(event.target.value);
}
writeValue(value: any): void {
console.log('writeValue() called');
this.value = value;
}
registerOnChange(fn: any): void {
this.propagateChange = fn;
}
registerOnTouched(fn: () => void): void {
this.propagateTouch = fn;
}
setDisabledState?(isDisabled: boolean): void {}
}
应用组件:
import { Component } from '@angular/core';
import { FormGroup, FormControl } from '@angular/forms';
@Component({
selector: 'my-app',
template: `
<div>
<button (click)="hidden = !hidden">{{hidden ? 'show' : 'hide'}}</button>
<button (click)="changeValue()">change value</button>
</div>
<div [formGroup] = "form" *ngIf="!hidden">
<simple-input-text formControlName="test"></simple-input-text>
</div>
`
})
export class AppComponent {
hidden = false;
form = new FormGroup({
test: new FormControl('')
});
changeValue(): void {
console.log('changeValue() called');
this.form.get('test').setValue('another value');
}
}
AppComponent
包含2个按钮:
- 显示/隐藏-重新调用
SimpleInputComponent
的生命周期 - 更改值-运行
FormControl.setValue
通过实现自己的writeValue
方法更新SimpleInputComponent
模拟场景:
- 点击"显示/隐藏"按钮(2n次(
- 打开控制台,单击"更改值"按钮
- 检查控制台,查看writeValue被调用(n+1(次(1次来自初始生命周期(
现场样品位于https://stackblitz.com/edit/angular-bgp3kg
所以问题是:我做错了什么吗?
我看到这个修复程序的PR仍然打开。你可以在这里查看。https://github.com/angular/angular/pull/29335.
平均而言,对于解决方案,您可以在SimpleInputComponent中定义ngOnDestroy,如下所示,
ngOnDestroy() {
if (this.ngControl) {
this.ngControl.reset();
this.ngControl.valueAccessor &&
(this.ngControl.valueAccessor.writeValue = () => {});
}
}