首页 > 解决方案 > ExpressionChangedAfterItHasBeenCheckedError:在 HTTP 加载拦截器中检查后,表达式已更改

问题描述

目标:

我试图在每个 ajax 调用上显示一个加载图标。为此,我添加了一个 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

代码:

应用组件.html:

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

应用组件.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() {}
}

标签: angular8angular-http-interceptorsangular-changedetection

解决方案


好的,我通过使用加载器将其添加到组件中来使其工作:

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


推荐阅读