Angular - 同一组件中的 ExpressionChangedAfterItHasBeenCheckedError

Sha*_*iel 7 angular-http-interceptors angular

我有一个简单的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);
  }
}
Run Code Online (Sandbox Code Playgroud)

我收到以下错误:

错误:ExpressionChangedAfterItHasBeenCheckedError:检查后表达式已更改。以前的值:'假'。当前值:“真”。

如果我是正确的,当子组件更改父组件中的值时会出现此错误。

但是我的子组件(HelloComponent)只执行 HttpGet 并且HttpInterceptor正在通过具有可观察的服务更改父组件(AppComponent)。

在这里更改服务不应该是正确的解决方案吗?有人可以解释为什么这不起作用吗?

Saj*_*ran 7

它会引发错误,因为您的变量加载在onLoadingChanged调用时会更新。这意味着您的初始值已更改。

我建议您阅读更多有关here

对此的一般解决方法是,

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


ber*_*ing 5

ExpressionChangedAfterItHasBeenCheckedError当表达式在一个更改检测生命周期内更改多次时,会抛出该异常(在开发人员模式下)。那么在你的例子中它是如何变化的呢?

生命周期#1:

  • loading: boolean = false;在构造函数期间AppComponent
  • hello 组件在onInit. 然后,这会触发更新可观察值的拦截器。

生命周期#2(在开发模式下,检查所有值是否稳定/仍然相同):

  • loading现在是true因为对 observable 的订阅改变了值,该值与之前的值不同loading = false,所以 Angular 抛出错误。

为了缓解该问题,您可以:

  • 将代码从onInit构造函数移至构造函数。缺点:不建议在构造函数中做一些事情
  • 包括 cdr,如 Sajeetharan 所描述的。缺点:您必须对您的可观察对象的每个客户端执行此操作。
  • 超时后发出更改后的可观察值setTimeout(() => myObservable.next(...));。缺点:很丑。但是,如果您希望多个客户端订阅您的 observable,这可能会给您带来最少的麻烦,因为您不必在任何地方都包含 cdr。

老实说,这些解决方案都不是特别漂亮。

请注意,async管道与另一个答案中建议的问题无关。