web-dev-qa-db-ja.com

Angular 4 ExpressionChangedAfterItHasBeenCheckedError

ParentComponent内 =>

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);
  }
}
18
Saurabh Kumar

記事 ExpressionChangedAfterItHasBeenCheckedErrorエラーについて知る必要があるすべて は、この動作を詳細に説明しています。

原因

あなたの問題は非常に似ています これに しかし、サービスを通じて親プロパティを更新する代わりに、同期イベントブロードキャストを通じて親プロパティを更新しています。 リンクされた回答 からの引用です:

ダイジェストサイクル中Angularは、子ディレクティブに対して特定の操作を実行します。そのような操作の1つは、入力を更新し、子ディレクティブ/コンポーネントでngOnInitライフサイクルフックを呼び出すことです。重要なのは、これらの操作が厳密な順序で実行されることです。

  • 入力を更新する
  • NgOnInitを呼び出す

あなたの場合、Angularは子コンポーネントの入力バインディングallItemsを更新し、次に子コンポーネントでonInitを呼び出し、親コンポーネントのallItemsを更新しました。これで、データの不整合が生じました。親コンポーネントには1つの値があり、子には別の値があります。 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>

要約:ポップする代わりに(配列の最後の要素を返し、削除してデータの状態を変更します)、通常の方法で状態を変更しないデータを取得して、例外を防ぎます。

0
Miko Chu