这里有两个解决方案!
1。将 ChangeDetectionStrategy 修改为 OnPush
对于这个解决方案,你基本上是在告诉 Angular:
停止检查更改;只有在我知道有必要的时候才会这样做
修改您的组件,使其使用ChangeDetectionStrategy.OnPush,如下所示:
@Component({
selector: 'app-child',
templateUrl: './child.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ChildComponent implements OnInit {
// ...
}
有了这个,事情似乎不再起作用了。那是因为从现在开始,您必须让 Angular 手动调用 detectChanges()。
this.cdr.detectChanges();
如果您有兴趣,请查看this article。它帮助我了解了ChangeDetectionStrategy 的工作原理。
2。了解 ExpressionChangedAfterItHasBeenCheckedError
这是来自this article 的关于此错误原因的一小段摘录,我已尝试仅包含有助于我理解这一点的部分。
完整的文章展示了关于此处显示的每一点的真实代码示例。
根本原因是角度生命周期的:
在每次操作之后,Angular 都会记住它曾经执行过的值
一个手术。它们存储在
组件视图。
在对所有组件进行检查之后,Angular 就会启动
下一个摘要循环,但不是执行操作,而是将当前值与它记住的值进行比较
上一个摘要循环。
在摘要周期检查以下操作:
检查传递给子组件的值是否与
将用于更新这些组件的属性的值
现在。
检查用于更新 DOM 元素的值是否与
将用于更新这些元素的值现在执行
一样。
检查所有子组件
因此,当比较的值不同时会引发错误。,博主Max Koretskyi 表示:
罪魁祸首总是子组件或指令。
最后是一些通常会导致此错误的实际示例:
在我的例子中,问题是动态组件实例化。
另外,根据我自己的经验,我强烈建议大家避免使用setTimeout 解决方案,在我的情况下会导致“几乎”无限循环(21 个调用,我不愿意向您展示如何激怒它们),
我建议始终牢记 Angular 生命周期,这样您就可以考虑每次修改另一个组件的值时它们会受到怎样的影响。 Angular 告诉你这个错误:
你可能做错了,你确定你是对的吗?
同样的博客还说:
通常,解决方法是使用正确的更改检测挂钩来创建动态组件
对我来说,一个简短的指南是在编码时至少考虑以下事项:
(我会随着时间的推移尝试补充它):
- 避免从其子组件修改父组件值,
而是:从它们的父级修改它们。
- 当您使用
@Input 和 @Output 指令时,请尽量避免触发生命周期更改,除非组件已完全初始化。
- 避免不必要的
this.cdr.detectChanges(); 调用,它们会触发更多错误,尤其是在处理大量动态数据时
- 当必须使用
this.cdr.detectChanges(); 时,请确保正在使用的变量 (@Input, @Output, etc) 在正确的检测挂钩 (OnInit, OnChanges, AfterView, etc) 处填充/初始化
- 如果可能,remove rather than hide,这与第 3 点和第 4 点有关。(更新:我今天注意到 Angular 建议删除而不是隐藏已从他们的网页中删除,here's the same quote but for angulardart)
- 避免在
setters 中使用@Input 注释的任何类型的逻辑,setter 在ngAfterViewInit 之前执行,因此很容易触发问题。
还有
如果你想全面了解 Angular Life Hook,我建议你阅读这里的官方文档:
https://angular.io/guide/lifecycle-hooks