2017-04-12 54 views
40

請向我解釋爲什麼我不斷收到此錯誤:ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked.ExpressionChangedAfterItHasBeenCheckedError解釋

很顯然,我只是把它在開發者模式,它不會對我的生產版本發生,但它是非常惱人的,我根本不要」不要理解在我的開發環境中出現錯誤的好處,這些錯誤不會出現在產品中 - 可能是因爲我缺乏理解。

通常情況下,修復是很容易的,我只是包裝誤差在這樣的的setTimeout造成代碼:

setTimeout(()=> { 
    this.isLoading = true; 
}, 0); 

或者強制檢測與這樣的構造變化:constructor(private cd: ChangeDetectorRef) {}

this.isLoading = true; 
this.cd.detectChanges(); 

但是,爲什麼我經常遇到這個錯誤?我想了解它,所以我可以在將來避免這些hacky修復程序。

+2

[你需要了解的ExpressionChangedAfterItHasBeenCheckedError錯誤的一切(https://開頭hackernoon .COM /一切任您需要到專門關於最expressionchangeda fterithasbeencheckederror-error-e3fd9ce7dbb4)很詳細地解釋了這種行爲 –

回答

2

當我瞭解了the Angular Lifecycle Hooks以及它們與變化檢測的關係後,我們瞭解了很多。

我試圖讓Angular更新一個綁定到*ngIf的全局標誌,並且我試圖在另一個組件的ngOnInit()生命週期鉤子內更改該標誌。

根據該文件,后角已檢測改變了這種方法被稱爲:

Called once, after the first ngOnChanges().

所以更新的ngOnChanges()裏面的標誌將不會發起變化檢測。然後,一旦變化檢測自然再次觸發,標誌的值已經改變並且錯誤被拋出。

就我而言,我改變了這一點:

constructor(private globalEventsService: GlobalEventsService) { 

} 

ngOnInit() { 
    this.globalEventsService.showCheckoutHeader = true; 
} 

要這樣:

constructor(private globalEventsService: GlobalEventsService) { 
    this.globalEventsService.showCheckoutHeader = true; 
} 

ngOnInit() { 

} 

,它解決了這一問題:)

30

這個錯誤表明你的應用程序存在一個真正的問題,因此拋出異常是有道理的。

devMode更改檢測在每次定期更改檢測運行後再增加一個轉向以檢查模型是否已更改。

如果模型已經常規和附加變化檢測匝之間改變時,這表示無論是

  • 變化檢測本身已經引起了變化
  • 的方法或吸氣每次都返回一個不同的值它被稱爲

這兩個都不好,因爲不清楚如何繼續,因爲模型可能永遠不會穩定下來。

如果角度運行改變檢測直到模型穩定下來,它可能會永遠運行。 如果Angular沒有運行變更檢測,則視圖可能不會反映模型的當前狀態。

又見What is difference between production and development mode in Angular2?

+1

我怎樣才能避免將來看到這個錯誤?我需要考慮一下我的代碼以確保不會犯同樣的錯誤嗎? –

+6

通常這是由一些像ngOnInit或ngOnChanges這樣的生命週期回調來修改模型引起的(一些生命週期回調允許修改別人不需要的模型,我不記得自己究竟是哪一個做或不做什麼)。不要綁定到視圖中的方法或函數,而是綁定到字段並更新事件處理程序中的字段。如果您必須綁定到方法,請確保它們始終返回相同的值實例,只要實際上沒有更改。變化檢測會調用這些方法很多。 –

+0

對於到達這裏的任何人使用ngx-toaster庫獲取此錯誤,這裏是錯誤報告:https://github.com/scttcper/ngx-toastr/issues/160 – rmcsharry

9

這更是一個側面說明不是一個答案,但它可能會幫助別人。試圖讓一個按鈕的存在依賴於形式的狀態時,我偶然發現了這個問題:

<button *ngIf="form.pristine">Yo</button> 

據我所知,這句法導致被添加和基於DOM刪除按鈕條件。這又導致了ExpressionChangedAfterItHasBeenCheckedError

在我的情況下,修復(雖然我不要求掌握差的全面影響),是使用display: none代替:

<button [style.display]="form.pristine ? 'inline' : 'none'">Yo</button> 
+4

我的理解ngIf與樣式是ngIf在條件爲真之前不在頁面中包含HTML,因此可以稍微減少「頁面權重」,而樣式技術會導致HTML始終處於頁面中,並且只是隱藏或顯示form.pristine的值。 – user3785010

+0

謝謝。只是讓你知道這個「解決」了我的問題(就像你說的,我沒有意識到完整的後果),但我有同樣的設置:提交按鈕被禁用基於其他輸入字段。 – Devator

24

我有一個類似的問題。 看着lifecyle鉤子文檔,我將ngAfterViewInit更改爲ngAfterContentInit,它工作。

+1

在我的情況下,我仍然使用ngAfterViewInit,但我不得不將內容包含在超時函數內。例如:setTimeout(()=> {this.updateSizeView()}); –

+0

@PhilipEnc我的問題與DOM更改觸發的更改有關。當DOM發生變化時,QueryList對象(來自@ContentChildren屬性)將更新,並在更新調用方法內更改雙向綁定屬性。這造成了我遇到的問題。像上面顯示的那樣用'setTimeout'將這個改變包裝到兩個屬性中就是一個竅門。謝謝! – kbpontius

1

我在Ionic3(使用Angular 4作爲其技術堆棧的一部分)中出現了這種錯誤。

對我來說是這樣做的:

<ion-icon [name]="getFavIconName()"></ion-icon>

所以我想一個ion-icon的從pin類型有條件地更改爲remove-circle,每一個屏幕上進行操作的模式。我想我將不得不添加一個* ngIF來代替。

3

我正面臨同樣的問題,因爲組件中的某個數組正在改變值。但是,我沒有檢測到值更改的變化,而是將組件更改檢測策略更改爲onPush(它將檢測對象更改而不是更改值的更改)。

import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core'; 

    @Component({ 
    changeDetection: ChangeDetectionStrategy.OnPush 
    selector: - 
    ...... 
    }) 
0

就我而言,我在這個問題我spec文件,同時運行我的測試。

我不得不改變nfIf[hidden]

<app-loading *ngIf="isLoading"></app-loading>

<app-loading [hidden]="!isLoading"></app-loading>

相關問題