一个 ExpressionChangedAfterItHasBeenCheckedError 错误的解决过程

举报
汪子熙 发表于 2022/11/06 14:45:55 2022/11/06
【摘要】 问题描述我的 Component 里有一个 selectedPane 字段(第56行),作为数据源显示在 div 标签里(代码第47行):代码第 51 行,我使用 @ViewChild 这个 query,将第 45 行的 div 元素,查询出来并通过 52 行的 set 函数,赋给 this.selectedPane.运行时收到这条错误消息:ERROR Error: ExpressionC...

问题描述

我的 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,因此抛出异常:

if (ngDevMode && isInCheckNoChangesMode()) {
            // View engine didn't report undefined values as changed on the first checkNoChanges pass
            // (before the change detection was run).
            const oldValueToCompare = oldValue !== NO_CHANGE ? oldValue : undefined;
            if (!devModeEqual(oldValueToCompare, value)) {
                const details = getExpressionChangedErrorDetails(lView, bindingIndex, oldValueToCompare, value);
                throwErrorIfNoChangesMode(oldValue === NO_CHANGE, details.oldValue, details.newValue, details.propName);
            }
            // There was a change, but the `devModeEqual` decided that the change is exempt from an error.
            // For this reason we exit as if no change. The early exit is needed to prevent the changed
            // value to be written into `LView` (If we would write the new value that we would not see it
            // as change on next CD.)
            return false;
        }

这个错误在 Angular 官网的这个视频里有详细的解释:

如果在 Angular 框架执行完变更检测之后,再修改属性值,比如在 ngAfterViewInit 或者本文例子的 set 函数里,就会抛出这个异常。

解决方案

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

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

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

Promise.resolve().then(() => this.loading false);

错误消失了:

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

能实现同样的效果:

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。