ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked in HTTP loading interceptor

ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked in HTTP loading interceptor

目标:

我试图在每个 ajax call.To 上显示加载图标 为此,我添加了一个 HTTP 拦截器,当一个或多个请求正在进行时将变量设置为 true,当所有请求都在时设置为 false已经完成。 UI 测试此值并显示是否加载程序,具体取决于。

问题:

每次 ajax 调用都会抛出一个错误:

ExpressionChangedAfterItHasBeenCheckedError: 
Expression has changed after it was checked. 
Previous value: 'ngIf: [object Object]'. Current value: 'ngIf: true'.

具有可重现错误的简化 Stakckblitz:

https://stackblitz.com/edit/angular-h4rpfb

代码:

appcomponent.html:

<p *ngIf="loaderService.isLoading | async">
  Loading!
</p>
<p *ngIf="!(loaderService.isLoading | async)">
  Not Loading!
</p>
<button (click)="loadSomething()">Load Something</button>
{{matches|async}}

appcomponent.ts:

import { Component } from "@angular/core";
import { HttpClient } from "@angular/common/http";
import { LoaderService } from "./core";
import { Observable } from "rxjs";

@Component({
  selector: "my-app",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent {
  matches: Observable<any>;

  constructor(public loaderService: LoaderService, private http: HttpClient) {}

  loadSomething() {
    this.matches = this.http.get("https://jsonplaceholder.typicode.com/posts");
  }
}

loader.interceptor.ts:

import { Injectable } from '@angular/core';
import {
  HttpErrorResponse,
  HttpResponse,
  HttpRequest,
  HttpHandler,
  HttpEvent,
  HttpInterceptor
} from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import { LoaderService } from './loader.service';

@Injectable()
export class LoaderInterceptor implements HttpInterceptor {
  private requests: HttpRequest<any>[] = [];

  constructor(private loaderService: LoaderService) { }

  removeRequest(req: HttpRequest<any>) {
    const i = this.requests.indexOf(req);
    if (i >= 0) {
      this.requests.splice(i, 1);

    }
    console.log(i, this.requests.length);
    this.loaderService.isLoading.next(this.requests.length > 0);
  }

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    this.requests.push(req);
    this.loaderService.isLoading.next(true);
    return Observable.create(observer => {
      const subscription = next.handle(req)
        .subscribe(
        event => {
          if (event instanceof HttpResponse) {
            this.removeRequest(req);
            observer.next(event);
          }
        },
        err => { this.removeRequest(req); observer.error(err); },
        () => { this.removeRequest(req); observer.complete(); });
      // teardown logic in case of cancelled requests
      return () => {
        this.removeRequest(req);
        subscription.unsubscribe();
      };
    });
  }
}

loader.service.ts:

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { ReplaySubject } from 'rxjs/ReplaySubject';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';


@Injectable()
export class LoaderService {
    public isLoading = new BehaviorSubject(false);

    constructor() {}
}

好的,我通过加载程序将其添加到组件中使其工作:

changeDetection: ChangeDetectionStrategy.OnPush

所以 appcomponent.html 现在看起来像这样:

import { Component,ChangeDetectionStrategy  } from "@angular/core";
import { HttpClient } from "@angular/common/http";
import { LoaderService } from "./core";
import { Observable } from "rxjs";

@Component({
  changeDetection: ChangeDetectionStrategy.OnPush,
  selector: "my-app",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent {
  matches: Observable<any>;

  constructor(public loaderService: LoaderService, private http: HttpClient) {}

  loadSomething() {
    this.matches = this.http.get("https://jsonplaceholder.typicode.com/posts");
  }
}

示例:

https://stackblitz.com/edit/angular-n6fzjm