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");
}
}
示例:
目标:
我试图在每个 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");
}
}
示例: