簡體   English   中英

Angular - 同一組件中的 ExpressionChangedAfterItHasBeenCheckedError

[英]Angular - ExpressionChangedAfterItHasBeenCheckedError in same Component

我有一個簡單的Angular 5應用程序,它帶有一個HttpInterceptor ,它在發出請求時通知服務。 組件可以訂閱此服務以獲取此信息。

此處有錯誤的完整示例 => https://stackblitz.com/edit/angular-5b86se

在我的AppComponent 中,當我的子組件 ( HelloComponent ) 中發出請求時,我想顯示一個加載指示器。

import { Component } from '@angular/core';
import { LoadingIndicatorService } from './loading-indicator.service';

@Component({
  selector: 'my-app',
  template: `<div *ngIf="loading" style="color:red">pretend this is loading indicator</div>
<hello name="{{ name }}"></hello>`
})
export class AppComponent  {
  name = 'Angular 5';
   loading: boolean = false;

   constructor(private loadingIndicatorService: LoadingIndicatorService) { 

    loadingIndicatorService
      .onLoadingChanged
      .subscribe(isLoading => this.loading = isLoading);
  }
}

我收到以下錯誤:

錯誤:ExpressionChangedAfterItHasBeenCheckedError:檢查后表達式已更改。 以前的值:'假'。 當前值:“真”。

如果我是正確的,當子組件更改父組件中的值時會出現此錯誤。

但是我的子組件( HelloComponent )只執行 HttpGet 並且HttpInterceptor正在通過具有可觀察的服務更改父組件( AppComponent )。

在這里更改服務不應該是正確的解決方案嗎? 有人可以解釋為什么這不起作用嗎?

它會引發錯誤,因為在調用onLoadingChanged時您的變量加載會更新。 這意味着您的初始值已更改。

我建議你閱讀更多關於here

對此的一般解決方法是,

    constructor(private loadingIndicatorService: LoadingIndicatorService,private ref: ChangeDetectorRef) { 
    loadingIndicatorService.onLoadingChanged.subscribe(isLoading => {
       this.loading = isLoading
       this.ref.detectChanges();
    });
 }

AsyncPipe 來救援:

<div *ngIf="loadingIndicatorService.onLoadingChanged | async">Loading...</div>

記得把服務改成public。

編輯:還需要將 changeDetectionStrategy 設置為 OnPush。 工作閃電戰: https : //stackblitz.com/edit/angular-ay1ikq

至於為什么即使使用 AsyncPipe 在沒有 OnPush 的情況下也會發生這種情況,也不能說。 也許有人對 Angular 核心有深入了解? CD 在攔截器之前/之后內部觸發? 我幾乎總是使用 OnPush 但必須有一種情況,我一開始沒有使用,並且仍然沒有看到 AsyncPipe 發生此錯誤。 唯一與我所做的任何事情真正不同的是使用攔截器來做一些導致綁定到模板的值發生變化的事情,所以這一定是根本原因?

當表達式在一個更改檢測生命周期中更改一次以上時,將引發ExpressionChangedAfterItHasBeenCheckedError (在開發人員模式下)。 那么在你的例子中它是如何變化的?

生命周期 #1:

  • loading: boolean = false; AppComponent的構造函數中
  • hello 組件在onInit發出 http 請求。 然后觸發更新可觀察對象的攔截器。

Lifecycle #2(在開發模式下,檢查所有值是否穩定/仍然相同):

  • loading現在為true因為對 observable 的訂閱改變了值,這與之前的loading = false值不同,所以 Angular 拋出錯誤。

為了緩解這個問題,您可以:

  • 將代碼從onInit移動到構造函數。 缺點:不建議在構造函數中做任何事情
  • 包括 Sajeetharan 描述的 cdr。 缺點:您必須在可觀察對象的每個客戶端上執行此操作。
  • 在超時后發出更改的 observable 值setTimeout(() => myObservable.next(...)); . 缺點:丑。 但是,如果您希望多個客戶端訂閱您的 observable,這可能會給您帶來最少的麻煩,因為您不必在任何地方都包含 cdr。

老實說,這些解決方案都不是特別漂亮。

請注意, async管道與另一個答案中建議的問題無關。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM