这是一个示例:
@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');
})
}
}
偶尔出现错误的地方:
表达changedafterithasbeencheckederror:表达在检查后发生了变化。先前的值:"未定义"。当前价值:'foo'
这是由于可观察到的可以在同一tick上获得值的可观察到的双向绑定而导致的。我宁愿不将上面的逻辑包裹在setTimeout
上,因为它看起来像是黑客的,并且使控制流程复杂
在这里避免此错误怎么办?
ExpressionChangedAfterItHasBeenCheckedError
错误是否会造成不良影响,还是可以忽略?如果可以的话,更改检测器可以对其保持沉默,而不会污染控制台?
让我们首先解开双向数据绑定以简化说明:
<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');
这种情况属于Synchronous event broadcasting
错误的类别,这些错误在文章中解释了您需要了解的有关ExpressionChangedAfterItHasBeenCheckedError
错误的所有信息。
在处理父组件更改后,触发ngAfterViewInit
生命周期钩。与儿童组件相关的钩子顺序在您需要了解的有关变更检测的所有信息中都在Angular中进行了解释。现在,Angular记得App
组件的更改检测时,foo
的值为undefined
,但是在验证阶段,该值为foo
,由Bpp
组件更新。因此,它会产生错误。
在这里避免此错误怎么办?
我链接的文章中描述了修复程序和问题。如果您不想重新设计逻辑,则唯一的安全选择是异步更新。您还可以为父组件运行更改检测,但是它们可能导致无限循环,因为组件上的更改检测触发了组件的孩子的更改检测。
do expressionChangeDafterithasbeenCheckedError错误患有病态 效果还是可以忽略?
不良影响是您将在应用程序App.foo==='foo'
和视图{{foo}}===undefined
中具有不固定的状态,直到下一个消化周期迭代为止。该错误无法在开发模式下关闭,但不会在生产模式下出现。
角度应用的两个阶段在解释此错误的心理模型方面也非常好。