一个 ExpressionChangedAfterItHasBeenCheckedError 错误的解决过程

问题描述
我的 Component 里有一个 selectedPane 字段(第 56 行),作为数据源显示在 div 标签里(代码第 47 行):

代码第 51 行,我使用 @ViewChild 这个 query,将第 45 行的 div 元素,查询出来并通过 52 行的 set 函数,赋给 this.selectedPane.
运行时收到这条错误消息:
ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: ''. Current value: 'undefined'.

问题分析
从调试器里能看出,旧的 value 是 "", 新的 value 是 undefined,因此抛出异常:

这个错误在 Angular 官网的这个视频里有详细的解释:
如果在 Angular 框架执行完变更检测之后,再修改属性值,比如在 ngAfterViewInit
或者本文例子的 set 函数里,就会抛出这个异常。

解决方案
一种 StackOverflow 上经常提到的解决方案就是,使用异步更新
, 将值的修改推迟到下一次变更检测周期中执行:

使用 setTimeout
配合延迟为 0
的调用方式,使得这个更新对应的检测,发生在下一次浏览器的宏任务队列中。

采用立即执行的 Promise
可以达到同样的效果:


错误消失了:

另一种解决方案是,在 set 函数里修改了属性值之后,立即手动
触发一次 change detection:

能实现同样的效果:

版权声明: 本文为 InfoQ 作者【Jerry Wang】的原创文章。
原文链接:【http://xie.infoq.cn/article/ae4fc1711c3f0d13c0fd4b489】。文章转载请联系作者。
评论