我有一个 ControlValueAccesor 和一个 FormControl。我需要从控件的 valueChanges 创建一个新的可观察对象,并通过 AsyncPipe 在模板中使用它。因此,在 CVA 的 writeValue 中,我使用 setValue() 更新表单控件的值。
public isOdd$ = new Observable<boolean>();
nestedFc = new FormControl([null]);
writeValue() {
this.nestedFc.setValue(22);
}
ngOnInit() {
this.isOdd$ = this.nestedFc.valueChanges.pipe(
map((value) => value % 2 !== 0)
);
}
<span> <input [formControl]="nestedFc"/> </span>
<span *ngIf="{value: isOdd$ | async} as context"> {{context.value}}</span>
问题是,第一次调用 writeValue 时会触发 valueChanges,但异步管道不会“看到”这些更改,因此视图不会显示更新。
围绕此有几个 GitHub 问题:ng 11:patchValue、valueChanges 和异步管道 和 https://github.com/angular/angular/issues/40826.
基于上一个,我创建了一个 stackblitz 来重现该问题。如果 writeValue 使用 setTimeout 来修补表单控件的值,则会触发 valueChanges 并且异步管道“看到”更改。
这是正确的吗?除了使用setTimeout真的没有其他办法吗?
问题是, writeValue 在 ngOnInit 之前被调用,并且您对值的订阅发生了变化。 Observables 仅获取订阅后发出的值,除非它们有
shareReplay
管道。
因此您需要添加
setTimeout
或使用不同的钩子(如 ngAfterViewInit
)来设置值。
当您修补
custom form control
内的值时,您应该更喜欢在外部表单上设置该值,而不是在自定义表单控件内,这基本上可以消除错误。
您应该始终更喜欢在加载时修补自定义控件外部的值。
import { Component } from '@angular/core';
import { FormControl } from '@angular/forms';
@Component({
selector: 'app-bug-show',
template: `
<app-child [formControl]="fc"></app-child>
`,
})
export class BugShowComponent {
fc = new FormControl(3);
}
要消除
async
bug,只需在 ngOnInit
钩子末尾手动调用更改检测即可。
ngOnInit() {
console.log('on init');
...
this.cdr.detectChanges(); // <- changed here
}