Angular2 ExpressionChangedAfterItHasBeenCheckedError

时间:2017-10-19 19:26:30

标签: angular angular2-services

我已经阅读了有关此错误的文档,我想我明白为什么会出现这个问题。我试图找出一个适合我的替代架构时遇到了问题。

我网站的根组件(app.component)有一个" loading"组件就可以了。它是第三方组件(ngx-loading),它基本上会抛出一个加载指示器,向用户显示应用程序中发生了一些需要一点时间的事情。加载组件采用一个参数来告诉它是否显示:

<ngx-loading [show]="loading" [config]="{ backdropBorderRadius: '14px' }"></ngx-loading>

我有一个站点服务,为此保存数据,我的应用程序获取服务数据OnInit:

this.siteService.getLoading().takeUntil(this.ngUnsubscribe).subscribe(res => {this.loading = res})

关于这一切的好处是,我可以从我的应用程序中的任何位置更改站点服务值,并控制是否弹出此加载指示器。不好的是,我现在看到这个ExpressionChangedAfterItHasBeenCheckedError错误。

同样,我想我理解为什么会发生这种错误,但到目前为止我看到的解决方案不会让我继续使用这一个简单的组件来处理我所有的#34; loading&#34 ;调用。或者我错过了什么?

SiteService:

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

@Injectable()
export class SiteService {

  private currentRoute = new Subject<string>();
  private currentAction = new Subject<string>();

  private loading = new BehaviorSubject<boolean>(false);

  constructor() {
  }

  public menuState:string = 'in';
  toggleMenuPicker() {
    this.menuState = this.menuState === 'out' ? 'in' : 'out';
  }

  getCurrentRoute(): Observable<string> {
    return this.currentRoute.asObservable();
  }
  setCurrentRoute(route: string) {
    this.currentRoute.next(route);    
  }

  getCurrentAction(): Observable<string> {
    return this.currentAction.asObservable();
  }
  setCurrentAction(action: string) {
    this.currentAction.next(action);
  }

  getLoading(): Observable<boolean> {
    return this.loading.asObservable();
  }

  setLoading(show: boolean) {
    this.loading.next(show);
  }



}

app.component

import { Component, OnDestroy, AfterContentInit } from '@angular/core';
import { trigger, state, style, transition, animate} from '@angular/animations';
import { SiteService } from './site/site.service';
import 'rxjs/add/operator/takeUntil';
import { Subject } from 'rxjs/Subject';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
  animations: [
    trigger('menuState', [
      state('in', style({
        transform: 'translate3d(-100%, 0, 0)'
      })),
      state('out', style({
        transform: 'translate3d(0, 0, 0)'
      })),
      transition('in => out', animate('200ms ease-in-out')),
      transition('out => in', animate('200ms ease-in-out'))
    ])
  ]
})
export class AppComponent implements OnDestroy, AfterContentInit {
  private ngUnsubscribe: Subject<void> = new Subject<void>();

 public loading;

  constructor(public siteService: SiteService) {
  }

  ngAfterContentInit() {
    this.siteService.getLoading().takeUntil(this.ngUnsubscribe).subscribe(res => {this.loading = res})
  }

  ngOnDestroy() {
    this.ngUnsubscribe.next();
    this.ngUnsubscribe.complete();
  }



}

2 个答案:

答案 0 :(得分:1)

您可以使用ChangeDetectionStrategy.OnPush或ChangeDetectorRef.detach()来保留单个组件,同时避免错误。

以下是一种可能的解决方案:

@Component({
  selector: 'loader',
  template: `<ngx-loading [show]="loading"></ngx-loading>`
})
export class LoaderComponent {
 public loading;

 constructor(public siteService: SiteService, private changeDetectorRef: ChangeDetectorRef) {
   this.changeDetectorRef.detach();
   this.siteService.getLoading()
                   .takeUntil(this.ngUnsubscribe)
                   .subscribe(res => {
                                this.loading = res;
                                this.changeDetectorRef.detectChanges();
                    });       
  }
}

您可以在AppComponent模板中创建此组件并使用它。

答案 1 :(得分:0)

尝试强制change detection

this.siteService.getLoading()
                .takeUntil(this.ngUnsubscribe)
                .subscribe(res => {
                  this.loading = res;
                  this.ref.detectChanges(); // <-- force change detection
                });

有关详细信息,请参阅我的similar answer