簡體   English   中英

ExpressionChangedAfterItHasBeenCheckedError Angular

[英]ExpressionChangedAfterItHasBeenCheckedError Angular

我正在為我的項目集成一個加載指示器,其可見性將在 HTTP 攔截器服務中集中確定。 但是,在實施之后,我收到以下錯誤:

 ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'null'. Current value: 'true'.

以下是我精簡的文件:

應用程序組件.ts

import { SpinnerService } from '@app/services/spinner.service';
import { Subject } from 'rxjs';

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.scss']
})

export class AppComponent implements OnDestroy {
    title = 'My App';
    isLoading: Subject<boolean> = this.spinnerService.isLoading;

    constructor(public spinnerService: SpinnerService) {
    }

}

app.component.html

<div class="example-container" [class.example-is-mobile]="mobileQuery.matches">
    <router-outlet></router-outlet>
    <div *ngIf="isLoading | async" class="overlay">
        Loading
    </div>
</div>

旋轉器.service.ts

import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';

@Injectable()

export class SpinnerService {

    isLoading = new Subject<boolean>();

    constructor() {
    }

    show() {
        this.isLoading.next(true);
    }

    hide() {
        this.isLoading.next(false);
    }
}

攔截器.service.ts

import { Injectable } from '@angular/core';
import {HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpErrorResponse} from '@angular/common/http';
import {EMPTY, Observable, throwError} from 'rxjs';

import { environment } from '@env/environment';
import { finalize } from 'rxjs/operators';
import { SpinnerService } from '@app/services/spinner.service';

@Injectable()
export class JwtInterceptor implements HttpInterceptor {

    // snackBar: MatSnackBar;

    constructor(public spinnerService: SpinnerService) { }

    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        this.spinnerService.show();

        return next.handle(request).pipe(
            finalize(() => this.spinnerService.hide()),
        );
    }
}

最后示例用法在我的其他組件中如下:

        this.http.get(`${environment.apiUrl}/form/schema/days`)
            .subscribe(data => {
                console.log('done', data);
            });

我不知道我在這里做錯了什么,有人可以進一步說明嗎?

您的加載 state 應該被初始化(默認為 false)。

由於 Subject 在初始化時不訪問值,您可以將其更改為BehaviorSubject

isLoading = new BehaviorSubject<boolean>(false);

理想情況下,您應該在 NgOnInit 掛鈎內調用數據源,這可以防止出現此問題。 解決此問題的一種方法是使用 RXJS 延遲運算符使加載器值更改發生在當前組件初始化周期之外:

isLoadingSubject = = new Subject<boolean>();
isLoading: Observable<boolean> = this.isLoadingSubject.pipe( delay(0) );

我認為這是相關閱讀: https://blog.angular-university.io/angular-debugging/

更新為在代碼的第二行解壓一下:

Angular 具有變化檢測功能——這是一個由以下因素觸發的過程:

  • 輸入事件(點擊、鼠標懸停、鍵盤彈起等)
  • Ajax 請求
  • setTimeout() 和 setInterval()

然后,它會遍歷應用程序的組件樹,檢查所有數據綁定(受 CD 策略等約束)。

您看到的錯誤發生在相同的更改檢測周期內 Angular 檢測到綁定值在對該綁定執行更改檢查后已經更改,因此導致 Angular 出現問題,因為它無法相應地更新模板。

因此,要解決這樣的問題,我們可以確保更改在特定 CD 周期開始之前發生(有時我們可以通過將數據更改操作移動到不同的組件生命周期掛鈎來實現)或延遲它 - 確保更改將在下一個周期。

RXJS 的底層延遲使用 setTimeout() ,如上所述,它會觸發自己的另一個 CD 周期。 Subject 是一個 Observable,可以多播到多個 Observer。 所以我們 pipe 它具有“延遲”功能,因此我們為該特定更改保留了 CD 周期。

因此,如果我們學究氣——這更像是一種可行的解決方法。 但是以我們在這里運行的另一個 CD 周期為代價。

暫無
暫無

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

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