这是example:
@Component({
selector: 'my-app',
template: `
<div>
<h1>{{ foo }}</h1>
<bpp [(foo)]="foo"></bpp>
</div>
`,
})
export class App {
foo;
}
@Component({
selector: 'bpp',
template: `
<div>
<h2>{{ foo }}</h2>
</div>
`,
})
export class Bpp {
@Input('foo') foo;
@Output('fooChange') fooChange = new EventEmitter();
ngAfterViewInit() {
const potentiallyButNotNecessarilyAsyncObservable = Observable.of(null);
potentiallyButNotNecessarilyAsyncObservable.subscribe(() => {
this.fooChange.emit('foo');
})
}
}
偶尔会出现错误:
ExpressionChangedAfterItHasBeenCheckedError:表达式在检查后发生了变化。以前的值:&#39; undefined&#39;。当前价值:&#39; foo&#39;
这是因为双向绑定由一个可以获得相同tick上的值的observable更新。我宁愿不用setTimeout
包装上面的逻辑,因为它看起来像一个黑客并使控制流复杂化
可以采取哪些措施来避免此错误?
ExpressionChangedAfterItHasBeenCheckedError
错误会产生不良影响还是可以忽略?如果可以的话,可以改变探测器是否静音而不污染控制台?
答案 0 :(得分:6)
让我们首先解开双向数据绑定以简化说明:
<div>
<h1>{{ foo }}</h1>
<bpp [foo]="foo" (fooChange)="foo=$event"></bpp>
</div>
它仍然具有相同的效果,偶尔会产生错误。只有potentiallyButNotNecessarilyAsyncObservable
是同步的,才会产生错误。所以我们也可以替换它:
ngAfterViewInit() {
const potentiallyButNotNecessarilyAsyncObservable = Observable.of(null);
potentiallyButNotNecessarilyAsyncObservable.subscribe(() => {
this.fooChange.emit('foo');
})
用这个:
ngAfterViewInit() {
this.fooChange.emit('foo');
此案例属于文章Everything you need to know about the ExpressionChangedAfterItHasBeenCheckedError
error中解释的Synchronous event broadcasting
类错误。
在处理父组件更改后触发ngAfterViewInit
生命周期挂钩。 Everything you need to know about change detection in Angular中解释了与子组件相关的挂钩顺序。现在Angular记得当它运行App
组件的更改检测时,foo
的值为undefined
,但在验证阶段,值为foo
,由孩子更新Bpp
组件。因此它会产生错误。
可以采取哪些措施来避免此错误?
修复和问题在我链接的文章中描述。如果您不想重新设计逻辑,那么唯一安全的选择是异步更新。您还可以对父组件运行更改检测,但它们可能会导致无限循环,因为组件上的更改检测会触发对组件子项的更改检测。
ExpressionChangedAfterItHasBeenCheckedError错误是否有效 效果还是可以忽略?
不良影响是,您将在应用程序App.foo==='foo'
和视图{{foo}}===undefined
中具有不稳定状态,直到下一次digest cycle次迭代。在开发模式下无法关闭错误,但它不会出现在生产模式中。
Two Phases of Angular Applications在解释此错误的心理模型方面也非常出色。