1. 程式人生 > 其它 >一個 ExpressionChangedAfterItHasBeenCheckedError 錯誤的解決過程

一個 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,因此丟擲異常:

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);
![](https://img-blog.csdnimg.cn/img_convert/25322fe43c081d144f8af96da180ec72.png)

![](https://img-blog.csdnimg.cn/img_convert/7b4957cd8b42d1ec79e0bd7d16271953.png)

錯誤消失了:
![](https://img-blog.csdnimg.cn/img_convert/bea6d17d6dbde5f7ae5e3c136baa2afe.png)

另一種解決方案是,在 set 函式裡修改了屬性值之後,立即`手動`觸發一次 change detection:
![](https://img-blog.csdnimg.cn/img_convert/2238d967cfff2194184077f41e21d5e8.png)

能實現同樣的效果:
![](https://img-blog.csdnimg.cn/img_convert/31094ef2caa2e58c89a091e4c888e735.png)