簡體   English   中英

Angular 4 ExpressionChangedAfterItHasBeenCheckedError

[英]Angular 4 ExpressionChangedAfterItHasBeenCheckedError

在父組件中=>

ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: ''. Current value: '[object Object]'.
            at viewDebugError (vendor.bundle.js:8962)
            at expressionChangedAfterItHasBeenCheckedError (vendor.bundle.js:8940)

父組件 Html

<div>
  <app-child-widget [allItems]="allItems" (notify)="eventCalled($event)"></app-child-widget>
<div>

父組件

export class ParentComponent implements OnInit {

  returnedItems: Array<any> = [];
  allItems: Array<any> = [];

  constructor(
  ) { }

  ngOnInit() {
     this.allItems = // load from server...
  }

  eventCalled(items: Array<any>): void {
    this.returnedItems = items;
  }
}

子組件

@Component({
  selector: 'app-child-widget',
  templateUrl: 'child.component.html',
  styleUrls: ['./child.component.css']
})
export class ChildComponent implements OnInit {
  @Output() notify: EventEmitter<any> = new EventEmitter();
  @Input() private allItems: Array<any>;

  constructor() { }

  ngOnInit() {
    doSomething();
  }

  doSomething() {
    this.notify.emit(allItems);
  }
}

文章關於ExpressionChangedAfterItHasBeenCheckedError錯誤你需要知道的一切都非常詳細地解釋了這種行為

原因

您的問題與問題非常相似但不是通過服務更新父屬性,而是通過同步事件廣播更新它。 這是鏈接答案中的引用:

在摘要循環期間,Angular 會對子指令執行某些操作。 其中一項操作是更新輸入並調用子指令/組件上的 ngOnInit 生命周期鈎子。 重要的是這些操作是按照嚴格的順序執行的:

  • 更新輸入
  • 調用 ngOnInit

因此,在您的情況下,Angular 更新了子組件上的輸入綁定allItems ,然后在子組件上調用了onInit ,這導致了對父組件的allItems的更新。 現在你有數據不一致。 父組件有一個值,而子組件有另一個值。 如果 Angular 繼續同步更改,您將獲得無限循環。 這就是為什么在下一個更改檢測周期 Angular 檢測到allItems已更改並引發錯誤的原因。

解決方案

這似乎是一個應用程序設計缺陷,因為您正在更新父組件和子組件的details 如果不是,那么您可以通過像這樣異步發出事件來解決問題:

export class ChildComponent implements OnInit {
  @Output() notify: EventEmitter<any> = new EventEmitter(true);
                                                        ^^^^^^-------------

但是你必須非常小心。 如果您使用任何其他鈎子,如在每個摘要循環中調用的ngAfterViewChecked您最終將陷入循環依賴!

就我而言,我正在更改數據的狀態——這個答案要求你閱讀 AngularInDepth.com 對摘要循環的解釋——在 html 級別內,我所要做的就是像這樣改變我處理數據的方式:

<div>{{event.subjects.pop()}}</div>

進入

<div>{{event.subjects[0]}}</div>

總結:不是彈出——它返回並刪除數組的最后一個元素從而改變我的數據的狀態——我使用了正常的方式來獲取我的數據而不改變它的狀態從而防止異常。

當出現此錯誤時,您可以使用rxjs timer將調用延遲一小段時間。

此示例使用@angular/material stepper從 url 中的查詢參數加載默認步驟。

import { timer } from 'rxjs'

@Component({
  template: `
    <mat-horizontal-stepper #stepper>
      <mat-step></mat-step>
      <mat-step></mat-step>
      <mat-step></mat-step>
    </mat-horizontal-stepper>
  `
})
export class MyComponent {
  @ViewChild('stepper', { static: true })
  private stepper: MatHorizontalStepper

  public ngOnInit() {
    timer(10).subscribe(() => {
      this.activatedRoute.queryParams.subscribe(params => {
        this.stepper.selectedIndex = parseInt(params.step || 0)
      })
    })
  }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM