web-dev-qa-db-ja.com

Angular 4: `ExpressionChangedAfterItHasBeenCheckedError:式はチェック後に変更されました

子モジュールのすべてのEventEmiiterでこのエラーが発生し、これに対する修正が見つかりません。

ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'true'. Current value: 'false'.

これが私のEventEmitterをトリガーします:

ngOnChanges(changes: any) {
    if (changes.groupingTabValid) {
        if (changes.groupingTabValid.currentValue !== changes.groupingTabValid.previousValue) {
            this.groupingTabValidChange.emit(this.groupingTabValid);
        }
    }
}

これが私の「メイン」コンポーネントのHTMLです

<year-overview-grouping [definitionDetails]="definitionDetails"
                        [fixedData]="fixedData"
                        [showValidation]="showValidation"
                        [groupingTabValid]="groupingTabValid"
                        (groupingTabValidChange)="setValidators('groupingTab', $event)">

</year-overview-grouping>

この関数を呼び出す

public setValidators(validator: string, e: boolean) {
    switch (validator) {
        case "groupingTab":             
            this.groupingTabValid = e;
            break;

        case "selectionTab":
            this.selectionTabValid = e;
            break;
    }

    if (this.groupingTabValid && this.selectionTabValid) {
        this.valid = true;
    } else {
        this.valid = false;
    }
}

1)簡単な説明では、このエラーの原因は何ですか?

2)これを解決するためにどのような手順を実行できますか?

15
Nicolas

一方向のデータフロールールに従う

SetTimeoutを使用して1ティックの間発信するように呼び出しを延期してみてください

if (changes.groupingTabValid.currentValue !== changes.groupingTabValid.previousValue) {
    setTimeout(() => this.groupingTabValidChange.emit(this.groupingTabValid), 0)
}
11
Draeken

ChangeDetectionStrategyをインポートして、コンポーネントに追加するだけです

@Component({
  changeDetection: ChangeDetectionStrategy.OnPush,
  selector: 'app-bla-bla-bla',
  templateUrl: './xxxxx'
})
4

setTimeoutを使用せずに、Angularが初期チェック後に変更が行われようとしていることを伝えることができます。

コンポーネントにChangeDetectorRefを注入し、そのmarkForCheckメソッドを使用できます。

/* ... */

constructor(private cdr: ChangeDetectorRef) {}

/* ... */

if (changes.groupingTabValid.currentValue !== changes.groupingTabValid.previousValue {
  this.cdr.markForCheck();
  this.groupingTabValidChange.emit(this.groupingTabValid);
}
2

私はこの例外タイプを自分で調べ始めたばかりなので、専門家はいませんが、フィードバックループを止めるためにそこにいると仮定します。

値を子に送信して親に戻す理由は明らかではありませんが、子が追加のロジックを適用すると仮定します。私には、子コンポーネントではなく、サービスに追加のロジックを適用する方が良いようです。

プランカーコードにはループがありませんが、Angularメカニズムはかなり単純に思えます-サイクルの最後に値を再チェックするだけです。注、groupingTabValidは、質問コードで直接フィードバックを行います。

与えられたプランカーコードを見ると、構造的に年齢の変更は親で処理できます。クリックイベントは(click)=changeAge(age.value)およびそれを処理するメソッド(親)

changeAge(inputAge) {
  this.newAge = inputAge;
  this.person.age = inputAge;
}

これがプランカーのフォークです。 修正されたプランカー
子コンポーネントは引き続きperson.ageを更新しますが、値は親に設定された値と同じであるため、エラーは発生しなくなりました。

1
Richard Matsen