"Expression has changed after it was checked"子组件在 ngOnInit 上发出时



我有一个父组件,它显示验证错误列表,并使用ngFor呈现子组件列表。每个子组件在ngOnInit期间执行其验证,并将结果输出给父组件。父组件侦听此输出并更新验证错误列表,其结果抛出错误:

Expression has changed after it was checked

现在,我明白了为什么会抛出这个错误——在更改检测周期开始时,验证错误处于一种状态,而在结束时,它处于另一种状态——这是不允许的。

我不明白的是如何绕过这个问题。父组件必须在页面顶部显示一个错误列表,每个子组件都会将其验证结果添加到此列表中。如果这违反了单向数据流,请告诉我如何以干净的方式绕过这一点(即,不将验证包装在setTimeout中,不从不可变列表更改为可变列表,以及在验证后不显式再次调用更改检测器)。

Plunker再现该问题:https://plnkr.co/edit/q52A1DraNOnxZa0qGFDo?p=preview


编辑

我通过构造带有isAsync标志的EventEmitter"解决"了这个问题:

new EventEmitter(true)

这意味着值将异步发出,因此发出的值将在下一个更改检测周期中拾取。我想结果与在setTimeout中封装逻辑相同,但通过这种方式,至少我们不必在任何可能发出值的地方都在超时中封装代码。

Angular不喜欢变化检测本身导致变化,而ngOnInit是由变化检测调用的。

在devMode中,更改检测在每次常规更改检测运行后都会进行额外的更改检测,以检查模型是否稳定。如果模型在更改检测过程中发生了更改,则会抛出此错误。

您可以将更改延迟到使用完成更改检测之后

ngOnInit() {
setTimeout(() => {
this.output.emit({value: true});
}
}

Plunker示例

更新(请参阅下面Ghetolay的评论)

ngOnInit不是由更改检测调用的,您可以在其中绑定更改。您不能从子级的ngOnInit对父级进行更改,因为这会中断单向数据流。

另请参阅https://github.com/angular/angular/issues/10131#issuecomment-233369491

您不允许更改ngOnInit中的任何内容。Angular 2团队拒绝将其视为一个bug(#1031),所以是的,您必须以不干净的方式进行:setTimeout(..., 0)Promise.resolve().then(...)

在子组件中一如既往,但在父组件中我是这样做的(只有当传入值不同时才会触发更改检测):

getStatus(status: StatusModel) {
if (this.currentStatus !== status) {
this.currentStatus = status;
this.changeDetector.detectChanges();
}
}

最新更新