angular - Angular QueryList 更改事件 ExpressionChangedAfterItHasBeenCheckedError
问题描述
订阅 QueryList 更改事件以及对事件做出反应并更改视图绑定到的属性时。我得到一个 ExpressionChangedAfterItHasBeenCheckedError 并且该值就像旧值一样。
我做了一个 stackblitz示例来显示错误。
我可以通过在队列中添加一个微任务来解决它。但我只是想了解正在发生的事情。
谢谢
解决方案
这就是发生的事情;
在开发模式下,会发生两个连续的 CD 周期,如本文的相关变更检测操作部分所述;
一个正在运行的 Angular 应用程序是一个组件树。在变更检测期间,Angular 对每个组件进行检查,这些组件包括按指定顺序执行的以下操作:
- 更新所有子组件/指令的绑定属性
- 在所有子组件/指令上调用 ngOnInit、OnChanges 和 ngDoCheck 生命周期挂钩
- 更新当前组件的 DOM
- 为子组件运行更改检测
- 为所有子组件/指令调用 ngAfterViewInit 生命周期钩子
- 单击添加按钮后,当单击回调完成执行更改检测时开始。
comp
有 1 个元素 - 在第一个 CD 循环期间;DOM 在第 3 步更新。元素
comp
添加到 DOM 并{{ count.length }}
投影到 DOM 为 0 @ViewChildren('comp') test: QueryList<ElementRef>
在第 5 步之前更新test: QueryList
。有 1 个元素QueryList.changes
observable 在设置查询的同时发出ViewChildren
,就在第 5 步之前。this.count.push(this.test.length)
被执行并count.length
变为 1。ngAfterViewChecked
被执行并且第一张CD(我们调查的相关部分)结束- 第二个 CD 循环开始。在此周期中,角度检查第一个 CD 周期的先前值与当前值。
- 在此检查期间,它遇到
{{ count.length }}
投影到 DOM 为 0 但现在count.length
为 1。此时它抛出异常。
解释更多;更改{{ count.length }}
为{{ count }}
不会引发错误,因为对象引用没有改变。
相似地; 更改{{ count.length }}
为{{ comps.length }}
也不会引发错误,因为comps.length
在第一个 CD 周期开始之前也是 1。
我还创建了一个演示,用于打印与我们的调查相关的组件生命周期中的相关步骤。您可以看到在第 4 个 CD 周期开始之前引发了异常。(第 1 次和第 2 次 CD 循环发生在单击按钮之前,因此在演示中应观察第 3 次和第 4 次 CD 循环)。还要注意QueryList.changes
发出值的点。
https://stackblitz.com/edit/angular-tmcttm
最后,正如您所说,解决此特定问题的方法是通过更改此行将微任务添加到队列中
this.count.push(this.test.length);
进入这个
setTimeout(_ => this.count.push(this.test.length));
推荐阅读
- c - c 用 cairo 和 cups 打印更多页面
- python - vscode:在调试控制台中评估多个选定的行会导致缩进错误(使用 python 类)
- elasticsearch - 在 ElasticSearch 的另一个字段中复制一个字段的数据
- python - Numpy 沿轴卷积 2 个二维数组
- javascript - 处理 javascript fetch 中的服务器端错误
- python - 从python中的多级(抓取)复杂结构json文件中提取密钥
- javascript - 在 React Table 中动态更改列标题名称
- acumatica - Acumatica - 处理页面上文本框的 PXGridColumn 属性
- python - 如何根据已知直线分割 OpenCV 轮廓?
- android - 为什么由服务制作的自定义视图没有 Decorview?