假设您有一个父组件 A 和一个子组件 B。A 组件具有名称和文本属性。在其模板中,它使用引用 name 属性的表达式:
template: '<span>{{name}}</span>'
并且它的模板中也有 B 组件,并通过输入属性绑定将 text 属性传递给该组件:
@Component({
selector: 'a-comp',
template: `
<span>{{name}}</span>
<b-comp [text]="text"></b-comp>
`
})
export class AComponent {
name = 'I am A component';
text = 'A message for the child component`;
...
}
所以这就是 Angular 运行变更检测时会发生的情况。它首先检查 A 组件。列表中的第一个操作是更新绑定,因此它将文本表达式评估为子组件的 A 消息并将其传递给 B 组件。它还将这个值存储在视图中:
view.oldValues[0] = 'A message for the child component';
然后它调用列表中提到的生命周期钩子。
现在,它执行第三个操作并将表达式 {{name}} 计算为文本 I am A 组件。它用这个值更新 DOM 并将评估的值放入 oldValues:
view.oldValues[1] = '我是一个组件'; 然后 Angular 执行下一个操作并对子 B 组件运行相同的检查。一旦检查了 B 组件,当前的摘要循环就完成了。
如果 Angular 在开发模式下运行,它会运行第二个摘要,执行我上面列出的验证操作。现在想象一下,在 Angular 将子组件的值 A 消息传递给 B 组件并存储它之后,A 组件上的属性文本以某种方式更新为更新后的文本。所以它现在运行验证摘要,第一个操作是检查属性文本是否未更改:
AComponentView.instance.text === view.oldValues[0]; // false '子组件的消息' === '更新的文本'; // false 但它有,所以 Angular 抛出错误 ExpressionChangedAfterItHasBeenCheckedError。
第三次操作也是如此。如果 name 属性在 DOM 中呈现并存储后更新,我们将得到相同的错误:
AComponentView.instance.name === view.oldValues[1]; // false '我是一个组件' === '更新的名称'; // 错误的
因此可以通过强制更改检测来解决错误:
ngAfterViewInit() {
this.cd.detectChanges();
}
取自链接:https ://indepth.dev/everything-you-need-to-know-about-the-expressionchangedafterithasbeencheckederror-error/