【问题标题】:Angular 7: ExpressionChangedAfterItHasBeenCheckedError while using Rxjs observblesAngular 7:使用 Rxjs observbles 时的 ExpressionChangedAfterItHasBeenCheckedError
【发布时间】:2019-10-22 11:50:02
【问题描述】:

我正在尝试使用 rxjs 主题在组件之间共享数据,并且我已经在组件中使用了该数据

Component.html

<div class="spinner-container"  *ngIf="loading">
      <div class="spinner-item">
          <nx-spinner nxSize="large"></nx-spinner>
      </div>
</div>  

component.ts

ngOnInit(){
    setTimeout(()=>{
      this.commonService.spinnerTrigger.subscribe((trigger)=>{
        this.loading = trigger;
     })
    },100)
}

这是错误

ExpressionChangedAfterItHasBeenCheckedError:表达式已更改 检查后。以前的值:'ngIf: false'。当前值: 'ngIf: true'。

我找到了使用changedetectref 的解决方法,但我不认为它的好做法是解决此问题的任何其他方法

【问题讨论】:

  • 如果您订阅 ngAfterViewInit 方法(没有超时),它是否有效?
  • 使用变更检测器是正确的解决方案。
  • @MichaelDesigaud nop 仍然是错误

标签: javascript angular rxjs


【解决方案1】:

您可以使用ChangeDetectorRefdetectChanges() 方法手动触发更改检测

试试这样:

import { ChangeDetectorRef} from '@angular/core';

constructor(private cdr: ChangeDetectorRef) { }

ngOnInit(){
    setTimeout(()=>{
      this.commonService.spinnerTrigger.subscribe((trigger)=>{
        this.loading = trigger;
        if (this.cdr && !(this.cdr as ViewRef).destroyed) {
           this.cdr.detectChanges();
        }
     })
    },100)
}

【讨论】:

  • 这有效,但有时我收到ViewDestroyedError: Attempt to use a destroyed view: detectChanges 错误
  • @iambatman 添加此条件if (this.cdr &amp;&amp; !(this.cdr as ViewRef).destroyed)
【解决方案2】:

使 next 回调异步对我有用一次:

this.commonService.spinnerTrigger.subscribe(async (trigger) => {
  this.loading = await trigger;
});

或添加零延迟:

this.commonService.spinnerTrigger.pipe(delay(0)).subscribe((trigger) => {
  this.loading = trigger;
});

【讨论】:

    【解决方案3】:

    这是 Github 中的一个未解决问题,

    Github 问题 => https://github.com/angular/angular/issues/15634

    他们目前使用setTimeout() 提供了一种解决方法,但仍然没有关于此问题的任何更新。

    您也可以尝试changeDetector,这可能会解决您的问题。

    import { ChangeDetectorRef } from '@angular/core';
    
    constructor(private cdRef:ChangeDetectorRef) {}
    
    ngAfterViewChecked()
    {
      this.cdRef.detectChanges();
    }
    

    【讨论】:

      【解决方案4】:

      我认为这里没有必要搞乱更改检测/setTimeout(触发更改检测)。

      Stackblitz

      使用父母和孩子都可以使用的微调器服务。

      spinner.service.ts

      @Injectable()
      export class SpinnerService {
        private loading = new BehaviorSubject<boolean>(true)
        loading$: Observable<boolean> = this.loading.asObservable()
      
        setSpinner(bool: boolean) {
          this.loading.next(bool)
        }
      }
      

      示例 - 组件设置微调器

        ngOnInit() {
          this.service.getChildData().pipe(
            // handle any errors
            catchError(err => {
              console.log('Error caught: ', err)
              this.data = err
              return throwError(err)
            }),
            // no matter what set spinner false
            finalize(() => {
              this.spinnerService.setSpinner(false)
            }),
            // subscription clean up
            takeUntil(this.destroyed$) 
          ).subscribe(data => this.data = data)
        }
      

      示例 - 显示微调器的父/容器

        ngOnInit() {
          this.loading$ = this.spinnerService.loading$
          this.spinnerService.setSpinner(true) // if needed
        }
      
          <div *ngIf="loading$ | async">
            I am a spinner
          </div>
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-11-07
        • 1970-01-01
        • 1970-01-01
        • 2017-11-25
        • 2018-01-20
        • 1970-01-01
        • 2019-07-03
        • 2017-12-14
        相关资源
        最近更新 更多