Angular: ExpressionChangedAfterItHasBeenCheckedError:ng4でチェックされた後に式が変更されました

作成日 2017年06月16日  ·  201コメント  ·  ソース: angular/angular

私は提出しています...


[ ] Regression (behavior that used to work and stopped working in a new release)
[X ] Bug report #14748 
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question

現在の動作


asyncを使用してテンプレートで監視可能なオブジェクトを使用した後、次のメッセージが表示されます。
エラーエラー:ExpressionChangedAfterItHasBeenCheckedError:チェック後に式が変更されました。 以前の値: ''。 現在の値:「何か」

予想される行動

あなたの環境について教えてください

<br i="15"/>
Angular version: 4.2.2

<p i="16">Browser:</p>

<ul i="17">
<li i="18">[X] Chrome (desktop) version Version 58.0.3029.110 (64-bit)</li>
</ul>

<p i="19">For Tooling issues:</p>

<ul i="20">
<li i="21">Node version: v6.10.3</li>
<li i="22">Platform: Mac</li>
</ul>
core regression bufix

最も参考になるコメント

4.1.3から4.2.3にアップグレードした後も同じ問題が発生します。 setTimeoutを使用すると、問題が修正されます。

から:

PanictUtil.getRequestObservable()。subscribe(data => this.requesting = data);

に:

PanictUtil.getRequestObservable()。subscribe(data => setTimeout(()=> this.requesting = data、0));

全てのコメント201件

reduxの使用でこれと同じ問題がありました。 これが私が彼らのリポジトリに投稿した問題へのリンクです。 角度2ではエラーはありませんが、角度4.2.2ではエラーをチェックした後に式が変更されます。
https://github.com/angular-redux/store/issues/428

4.1.3から4.2.xにバンプすると、コンポーネントに問題が発生します。 4.1.3に戻すと問題は解決しますが、それは私が永遠に維持したいものではありません。

最小限のプロジェクトで再現できましたが、この(おそらく)リグレッションを修正する方法をまだ模索しています。 しかし、ダーティチェックが開発で実施されているようです。 4.2.xからのモード。 しかし、変更ログはそれについて明確ではありません。 ヒントは大歓迎です。

4.1.3から4.2.3にアップグレードした後も同じ問題が発生します。 setTimeoutを使用すると、問題が修正されます。

から:

PanictUtil.getRequestObservable()。subscribe(data => this.requesting = data);

に:

PanictUtil.getRequestObservable()。subscribe(data => setTimeout(()=> this.requesting = data、0));

@jingglang http://plnkr.co/edit/tpl:AvJOMERrnz94ekVua0u5で、できるだけ最小限のコードで問題を再現してみてください。

同じ問題が発生しています-2.4から4.2にアップグレードしましたが、非表示/表示ロジックの一部が壊れています。 これはアコーディオンスタイルのロジックであり、可視性は、現在のパネルを、(とりわけ)どのパネルを可視化するかを保持するオブザーバブルと比較することに依存します。 コンポーネントは@selectを使用してオブザーバブルを取得し、|でバインドされます。 テンプレートのasync-2.4ではチャームのように機能し、ExpressionChangedAfterItHasBeenCheckedErrorを取得し、パネルは最初のクリックで非表示および表示されず、2回目のみ表示されます。

4.1.3から4.2.2に更新すると、同じ問題が発生します。

しかし、私は回避策を見つけました。
ChangeDetectionRef挿入し、エラーポイントでdetectionChanges()関数を呼び出します。

constructor(private cdr: ChangeDetectionRef) {
}

onChange(): void {
  this.val += 1; // <- get error after
  this.cdr.detectionChanges();
}

これは4.2で行われたフォロー変更の問題だと思います

https://github.com/angular/angular/pull/16592

この問題を解決するには: https

ng-contentタグ内にコンテンツはありますか?

ng-contentタグ内のフォームでも同じ問題が発生し、ContentChangedAfterエラーでエラーが発生します。

4.1.3から4.2.0以降に更新すると同じ問題が発生します

バグを再現するための小さなプロジェクトを添付します

app-error.zip

その後、(上記のアドバイスに従って)4.1.3にダウングレードするとエラーが解消されるため、競合が何であれ、4.2に固有です。 今週(または来週)にプランカーをまとめる時間がありませんが、ストア上の2つの別々のオブザーバブルを更新する単一のアクションに関連しているようです。それぞれがUIに影響を与えます。 UIの更新の1つが発生し、もう1つが例外を引き起こします。

うーん、
間違いなくバージョン4.1.3にロールバックすると問題が解決し、@ jingglangで示されるようにタイムアウトも適用されます。

こんにちは@tytskyi。 この問題のためにplunkrhttp //plnkr.co/edit/XAxNoV5UcEJOvsAbeLHTを作成しました。 それが役立つことを願っています。

@jingglangによって与えられた回避策は(私にとって)4.2.0以降で機能します
または、子の発行イベントをコンストラクターに変更しても、エラーは発生しなくなりました

@umensチェック後に親コンポーネントを更新するため、一方向のデータフローを維持しません

@alexzuzaどうすればそれを達成できますか? 私はいつもエラーなしでこの方法でそれをしました。 SetTimoutを配置してもエラーが発生しなくなった理由。 (プランカーを更新しました)ライフサイクルに干渉しているのではないかと思いますか?

こんにちは@umensお時間を@alexzuzaの言うことです:チェックされた後に親コンポーネントフィールドを更新します。 あなたのコードはこれとほぼ同等です: http
なぜそれが機能したのですか? おそらく偶然に、古いバージョンのangularまたはRxjsにバグがありました。 使用したバージョンを教えてください。 それとも、作業プランカーに入れますか?

SetTimoutを配置してもエラーが発生しなくなった理由。

フィールドを非同期に変更するため、一方向のデータフローが尊重されます。

反対のことから考えてみましょう:なぜエラーが発生するのですか? 変更検出の最初のチェックは上から下に行われます。 テンプレートにバインドされたapp.toolsConfig値をチェックします。 そして、それはレンダリング<child-cmp>同期的に更新するapp.toolsConfig 。 レンダリングが行われます。 これで、変更検出の2番目(開発モードのみ)のチェックを実行して、アプリケーションの状態が安定していることを確認します。 ただし、子をレンダリングする前のapp.toolsConfig app.toolsConfig 、後の

OK。 詳細な回答ありがとうございます。 子コンポーネントのライフサイクルがいつ発生するかについての情報が見つかりませんでした。
私が使用した以前の「動作中」バージョンの場合:
@ angular / *:4.1.1(compiler-cli-> 4.1.0を除く)
rxjs:5.3.1

ここの@umensは、あなたが言及した古いバージョンでエラーを再現しています: http//plnkr.co/edit/kfoKmigXzFXwOGb2wyT1?p = preview それはスローするので、おそらくいくつかのサードパーティの依存関係が動作に影響を与えたか、完全な複製命令ではありませんか?

わからない。
あなたがさらに進むことに興味があるなら、これが私のyarn.lockです: https ://pastebin.com/msARLta1
しかし、私は何が違うのか分かりません

4.1.2から4.2.3に切り替えた後、同様の「ExpressionChanged ...」エラーが表示されます。

私の場合、サービスを介して相互作用する2つのコンポーネントがあります。 このサービスは私のメインモジュールを介して提供され、 ComponentAComponentB前に作成されます。

4.1.2では、次の構文はエラーなしで問題なく機能しました。

ComponentAテンプレート:

<ul *ngIf="myService.showList">
...

ComponentBクラス:

ngOnInit() {
  this.myService.showList = false; //starts as true in the service
  ...
}

4.2.3では、「ExpressionChanged ...」エラーを回避するために、 ComponentAのテンプレートを次のように変更する必要があります。

ComponentAテンプレート:

<ul [hidden]="!myService.showList">
...

なぜこれが問題になっているのか、そしてなぜ*ngIfから[hidden]への切り替えが機能するのかを理解しようとしています。

私は同じ問題を抱えています、そしてそれは私がこのような非同期パイプを使用しているときに主に起こります:
<div>{{ (obs$ |async).someProperty }}</div>
私がこのようにそれを使うならば:

<div *ngIf="obs$ | async as o">
  <div>{{ o.someProperty }}</div>
</div>

その後、エラーが消えるので、以前は検出されなかったミスを多くの人が犯したからかどうかはわかりません。4.2でバグが発生したと思います…

私にとっても同じ問題です。 問題は@angular / routerから来ているようです

また、ルーターに関連しているという結論に達しました。 昨日も同じくらい投稿しようとしましたが、投稿に失敗したようです。 さらに調査し、コードを簡略化して、エラーを追跡できるかどうかを確認しました。 次のコードは、アクションの最終的なソースがパネルヘッダーのクリックである場合は完全に機能しますが、ルーティングを介してトリガーされると、ExpressionChangedエラーで失敗します。

<span class="glyphicon pull-right" [ngClass]="{'glyphicon-chevron-up' : (ui$ | async)?.visiblePanels[VisiblePanel.IngredientTypes], 'glyphicon-chevron-down' : !((ui$ | async)?.visiblePanels[VisiblePanel.IngredientTypes])}"></span>

これは私が問題を解決するのに役立ちました! 親の変更を明示的にトリガーする必要があります。

@acidghostのように、親コンポーネントからAfterViewInit ChangeDetectorRef#detectChangesを実行することで、この問題を解決できます。

plunkr reproをお持ちの場合は、gitterについて同様の苦情があり、常にユーザーの問題になるため、確認させていただきます。

バグが4.2で導入されたのか、それともバグが4.2未満でエラーがなく、4.2で解決されたのかはわかりません。

エラー:ExpressionChangedAfterItHasBeenCheckedError
解決:
componentRef.changeDetectorRef.detectChanges();

以下は私のコード(動的に生成されたコンポーネント)の一部です:
componentRef = viewContainerRef.createComponent(componentFactory); //コンポーネントが作成されました
(componentRef.instance).data = input_data; //ここでコンポーネントデータを手動で変更します
componentRef.changeDetectorRef.detectChanges(); //変更が検出されます

単純なコンポーネントの場合は、「componentRef」にアクセスして実行する方法をグーグルで検索してください
componentRef.changeDetectorRef.detectChanges();
コンポーネントデータ/モデルを設定/変更した後。

解決策2:解決策2:
ダイアログ「this.dialog.open(DialogComponent)」を開いているときに、このエラーが再び発生しました。
したがって、この開いているダイアログコードを関数内に配置し、その関数にsetTimeout()を適用すると、問題が解決しました。
例:
openDialog(){
dialogRef = this.dialog.open(DialogComponent);
}
ngOnInit():void {
setTimeout(()=> this.openDialog()、0);
}
それはハックのようですが..動作します!!!

4.2以降、同じ問題が発生し、解決策や回避策は機能しませんでした:( ngAfterViewInitメソッドのコンポーネントファクトリを使用してコンポーネントを動的に作成します。このコンポーネントは、 @Input()フィールドを持つディレクティブを使用します。これをバインドします。ディレクティブの@Input()フィールドは、コンポーネントのビューがビルドおよびチェックされ、静的文字列で初期化されるまで未定義のようです。

https://plnkr.co/edit/E7wBQXm8CVnYypuUrPZd?p=info

staticComponent.ts

export class StaticComponent implements AfterViewInit {
  @ViewChild('dynamicContent', {read: ViewContainerRef})
  dynamicContent: ViewContainerRef;

  constructor(private componentFactoryResolver: ComponentFactoryResolver) {}

  ngAfterViewInit(): void {
    const componentFactory: ComponentFactory<DynamicComponent> = this.componentFactoryResolver.resolveComponentFactory(DynamicComponent);
    this.dynamicContent.createComponent(componentFactory);
  }
}

test.directive.ts

@Directive({
  selector: '[testDirective]'
})
export class TestDirective {
  @Input()
  testDirective: string;
}

dynamic.component.ts

@Component({
  selector: 'dynamic-component',
  template: `<div [testDirective]="'test'">XXX</div>`
})
export class DynamicComponent {
}

エラーエラー:ExpressionChangedAfterItHasBeenCheckedError:チェック後に式が変更されました。 以前の値:「未定義」。 現在の値:「テスト」。 親と子がダーティチェックされた後にビューが作成されたようです。 変更検出フックで作成されていますか?

私はこのスレッドを読んで少し混乱しています。 これは既知のバグですか?今後のリリースで解決される予定ですか? または、これは予想される動作ですか? もしそうなら、解決策は何ですか? ありがとうございました。

それが関係しているかどうかはわかりませんが、私はこのメッセージを情熱的に嫌います! これは、すぐに使用できる「ngtest」コマンドを使用して単体テストを行う場合にのみ表示されます。 単純なサービスをうまくモックする方法について混乱する可能性があると思います。 また、他の誰もまだテストでこれを見たことがなく、テストに関連するものを見つけることができないことに驚いています。 このトピックと、ExpressionChangedAfterItHasBeenCheckedError +ユニットテストの検索を監視します。

私はplnkrをまとめようとしていて、エラーを再現するのが非常に難しいことに気づきました-しかし、大規模なデバッグにより、何が問題になっているのかを確認することに少し近づいたと思います(ただし、バグかどうかを判断するのではなく、または私の側の間違い)。

同じレデューサーのカットダウンバージョンを実際のアプリと同じ順序で組み合わせて、routerReducerを最後に使用して、小さなサンプルアプリを作成しました。 不満なことに、エラーは発生しませんでした。 そこで、ブレークポイントを設定し、レデューサーを介したアクションのフローを監視しました。 私が見つけたのは、2つのアプリが逆の順序でアクションをディスパッチしていたことです。

私のUIレデューサーは可視性を管理し、エラーを引き起こしている状態変化の原因です。 これは、レデューサーの縮小版であり、関連するアクションを示しています。


export const userInterfaceReducer = (state = INITIAL_UI(), action: any) => {
    switch (action.type) {
        case IngredientActions.LIST_INGREDIENTS:
            if (action.hideTypes) {
                return Object.assign(state, { visiblePanel: VisiblePanel.Ingredients });
            }
            return state;
        default:
            return state;
    }
}

これを小さなテストアプリで実行すると、機能します。最初に、LIST_INGREDIENTSアクションが実行され、新しい状態が返されます。 次に、@ angular-redux / router :: UPDATE_LOCATIONアクションが実行され、デフォルトのケースがヒットして、同じ状態が返されます。

実際のアプリで実行すると、順序やアクションが逆になります。 @ angular-redux / router :: UPDATE_LOCATIONアクションが最初に起動し、デフォルトから古い状態を返します。 次に、LIST_INGREDIENTSが起動し、新しい(変更された)状態が返されます。エラーが発生します。

私は自分が何か馬鹿げたことをしたという考えに完全にオープンであり、これは実際のバグではありません-しかし、もしそうなら、他の誰かが私がしたことを見ますか?

(脚注として、私は4.1.3バージョンをチェックしました...そしてそれはまた「正しい」順序でreduxレデューサーを起動します-つまり、場所の変更はリスト成分アクションの後に起動します、それはおそらくそれが機能する理由ですが、 4.2バージョンはそうではありません)。

1つのコンポーネントがサービスのプロパティを変更すると、同じ厄介なバグが発生します。 * ngIfを親コンポーネントに移動することで解決しました。 だからそれは間違いなくバグです

ngDoCheck()メソッドを使用し、ChangeDetectorRef.detectChangesメソッドを呼び出すことで、これを回避できました。

public ngDoCheck(): void {
    this.cdr.detectChanges();
}

@pappacurdsそうしないでください。 あなたはそれで無限の変化検出サイクルを引き起こしました。

ここで同じエラーが発生する可能性がありますhttps://plnkr.co/edit/IeHrTX0qil17JK3P4GBb?p=preview

エラー: previous undefined after undefiend

同じエラーを更新した後

わたしも

4.0.0で問題なく、4.2.6にアップグレードした後、同じエラーが発生します。

修正は明らかですが、更新によってユーザーアプリケーションが「サポート」されるようにフレームワークを変更すると、新しいマイナープラットフォーム更新が配線されます。 JSは互換性を保持するために古い間違いをまだ維持しています...

ここで同じエラー

この問題は~4.2.4ます。 ~4.1.3ダウングレードすると、問題が解決しました。

問題は、親のAfterViewInitの最後(変更を行っていた場所)でChangeDetectorRef detectChanges()を使用している場合でも、 ContentChildren値を変更することです。 奇妙なことに、この問題はコンポーネントでのみ発生し、ディレクティブでは発生しません。 4.2.4で作業していたディレクティブを、テンプレートが<ng-content></ng-content>コンポーネントに変換すると、 ExpressionChangedAfterItHasBeenCheckedErrorエラーがスローされ始めました。

これは、マテリアル2の入力コントロールで作業しているときに取得しました。 その値を設定しようとしました

ngAfterViewInit(): void {
this.numberFormControl.setValue(200);
}

ExpressionChangedAfterItHasBeenCheckedErrorエラーがAngular 4.2.x始まり始めた場合を示す簡単なPlunkerをまとめまし

親コンポーネントのngAfterViewInit ChangeDetectorRef detectChanges()を使用している場合でも、 ContentChild値を変更するとエラーが表示される単純なプランカーを作成しました。

編集:私の最初の例に基づいて、コンポーネントの代わりに親ディレクティブを使用し、エラーのない単純なプランカーも作成しました。

@saverettプランクで別のエラーメッセージが表示されます

@JSMike私はあなたのプランカーとまったく同じケースを持っていました( setTimeoutplunkerでContentChildを参照するコードをラップすることで回避しました

@JSMike頭を上げてくれてありがとう。 angle / zone.js#832の問題のようです。 最新のzone.jsが修正されるまで、zone.jsのバージョンを0.8.12に設定しました。 プランカーは再び機能するはずです。

@matkokvesicは実際には解決策ではなく、ライフサイクルフックの外部で変更が発生するだけです。 提供されているコード例は、Angularv4.1.3を使用しているときにエラーを引き起こしません

@JSMike同意します、これは一時的な回避策です。 参照される要素使用してContentChildrenngAfterViewInit罰金前アンギュラ4.2.6働いていました。

コードでchangeDetection: ChangeDetectionStrategy.OnPushthis.changeDetector.markForCheck();を使用して修正しました

@touqeershafi this.changeDetector.markForCheck();を使用している場合、それはあなたが正しい方法で物事を使用していないことを意味するのではありません

これはまだ起こっています。 これは間違いなくバグであり、予期された動作ではないため、修正する必要があります。

@istitiこれは適切な方法ではないことは理解していますが、少なくとも一時的にこのエラーを取り除くことができますが、私のプロジェクトに関する限り、バグURLを含むTODOをすでに追加しています。

これはまだ起こっています。 これは間違いなくバグであり、予期された動作ではないため、修正する必要があります。

@rwngallegoに同意する思います... 4.2.xの更新前に、 @ ViewChildren@ ContentChildrenに依存し、これらを通じて親と子の関係を作成する、再利用可能な/ ng-contentstlyeコンポーネントを既にいくつか作成していました。チャネル(プロパティの取得/設定など)。

ChangeDetectorRefインスタンスをこれらすべてのコンストラクター(このコンポーネントを拡張する子クラスを含む)に常に挿入する必要があり、意図した動作や正しい動作のように感じられません。

@saverettのような動的コンポーネントを使用しているもの(ルーターを使用している場合は動的コンポーネントを使用している)の場合、 https://github.com/angular/angular/issues/15634に記載されている問題だと思い

ngAfterViewInit / ngAfterViewChecked内で変更を行う人にとっては、予想どおりだと思います。

他の人にとっては(ここではまだ見ていませんが)、それはまだ私には謎です。

これは、次のシナリオで私に起こっています。
子コンポーネントを使用する親コンポーネントがあります。 子コンポーネントには、親が提供するデータが必要ですが、このデータは、親コンストラクターのpromise結果から取得されます。 そう:

constructor {
dataservice.getData().then((result) => {
this.source = result;
});
}

親テンプレート

<app-child [source]="source"></app-child>

子コンポーネント

@Input() source:any[];

このコードはこのエラーをスローしますが、これをコンポーネントに追加すると問題が解決しました。

constructor(private cdRef: ChangeDetectorRef) {
dataservice.getData().then((result) => {
this.source = result;
 this.cdRef.detectChanges();
});
    }

これは推奨される解決策ですか、それともバグの回避策ですか?そのような変更を行うことの意味は何ですか? 私には、Angularに変更を検出するように指示しているように見えますが、これは避けるべきだと思います...

@sulhome ...あなたの問題は、親のコンストラクターのpromiseが子コンポーネントの@Inputしていることです... CDサイクルと開発モードでの確認中に。 それがエラーの理由です。

this.cdRef.detectChanges();は2番目のCDサイクルを追加するため、確認が成功し、=>エラーが発生しません。

したがって、すべてが期待どおりに機能します。 これらのタイプの問題を回避するには、コードで適切な構造と一般的なロジックを使用することがどのように機能するかを理解することが重要です。 あなたはここの深い説明でそれについて読むことができます: https

@ mlc-mlapisだから、私はthis.cdRef.detectChanges();を追加することでそれを正しく行っていると言っているので、これは何の意味もありませんか?
子のngOnChanges聞いて、親からの思いました。 それはそれを行うための正しいアプローチではありませんか? 私はそれを実行し、まだエラーを受け取っていることに注意してくださいが、それがngOnChanges目的であると思いました

@sulhomeはい、 this.cdRef.detectChanges();が役立ちますが、CDが2枚あるという結果もあります...したがって、コンポーネントでOnPush戦略を使用して、オーバーヘッドを排除する方法と場所によって異なります。もう1枚のCDサイクル。

dataservice.getDataがhttpを使用してデータを取得することが事実である場合、おそらく最も簡単な解決策は、そのサービスでオブザーバブルを取得し、子コンポーネントからサブスクライブすることです。 したがって、データがストリームで送信されるとき、子供はそれに反応することができます。

記録のために@ghetolay 、それがバグである場合がいくつかあります、例えば私が開いた#18129を見てください。

@Outputを使用して子コンポーネントからデータを取得すると、この問題が発生します。

親コンポーネントhome.tsで

drag_second(trigger){ console.log("dragged222222"+trigger); if(trigger){ this.isactive=true; }else{ this.isactive=false; } }
およびhome.html
<div class="upper" [ngClass]="{'isactive':isactive}">

ドラッグスタートとドラッグエンドのときにisactiveが変更されます。

エラーメッセージは
ExpressionChangedAfterItHasBeenCheckedError:チェック後に式が変更されました。 以前の値: 'true'。 現在の値: 'false'。

または、クラス属性を変更/追加できる他の方法はありますか?

4.2.xと4.3.0でも同じ問題が発生しますが、4.1.xでは発生しません。

エラーは、 ng-templateを使用するルートでのみ発生します。

ソリューション:内のすべてのコードを置くngAfterViewInitsetTimeout (@jingglangに感謝/クレジット)私のための問題を解決しました。

public ngAfterViewInit(): void {
    setTimeout(() => {
        //my code
    });
}

@ Wernerson 、setTimeoutに関するJSMikeのコメントを見て

私の問題は、 ActivatedRouteparams Observableを使用してngOnInit()状態を変更したことRouterparams同期を放出しているように見えるので、更新はCD中に行われます。
これに対する私の簡単な解決策は現在次のとおりです。

  // !!! see delay(0)
  // results are consumed by async Pipe, where I also had the CD issue (null vs. undefined)
  this.results = this._activatedRoute.params.delay(0)
    .do(params => this.searchParameters = this._createSearchParameters(params))
    .switchMap(p => {
      let key = this.searchParameters.key();
      return this._searchResultCache.results.map(r => r.get(key));
    });

(これがこれまでで最も美しいリアクティブ/関数型コードではないことを私は知っています;))
したがって、解決策は、パラメータの放出を1 tick delay(0)することtick

私が同様の問題を抱えている他の人を助けることができることを願っています。
また、私の解決策がどのように間違っているか危険であるかという懸念も歓迎します。

この問題の状況はどうなっていますか? それはAngular4.2.6でも明らかです。
これは本番ビルドの問題ですか? このエラーはデバッグビルドでのみ発生するためです。

変更検出を手動で実行したり、setTimeout()で実行したりすることで、すべての回避策が嫌いです。

setTimeout()は、アプリケーション全体で新しい変更検出サイクルを引き起こすため、非効率的ですが、 detectChanges()は現在のコンポーネントAFAIKのみをチェックします。

私のチームは、ngOnInitが何であるかとそうでないかについて中核的な誤解を持っていました。 ドキュメントは言う

ngOnInit() :Angularが最初にデータバインドされたプロパティを表示し、ディレクティブ/コンポーネントの入力プロパティを設定した後、ディレクティブ/コンポーネントを初期化します。最初のngOnChanges()の後に1回呼び出されます。

ngOnInitから(アプリケーションの状態を追跡する)サービスにサブスクライブしていました。 私たちのサブスクリプションは同期BehaviorSubjectsです。 サブスクリプション時に起動します。 したがって、「Angularが最初に...ディレクティブ/コンポーネントの入力プロパティを設定した後」、すぐに値を変更していました。 これは、それが問題であることを示す非常に単純なプランカーです。

Tour Of Heroesの例では、最終的に非同期のhttpを使用しているため、問題は発生しません。 これが私たちの混乱の原因だと思います。

現在行っているのは、代わりにコンストラクターから同期状態をサブスクライブすることです。これにより、コンポーネントの値がすぐに設定されます。 このスレッドの他の人は、タイムアウトを介して2番目の変更検出を開始するか、後で値を設定するか、Angularにそうするように明示的に指示することを提案しています。 そうすることでエラーが表示されなくなりますが、おそらく不要です。

@maximuskが共有した記事は必読です: ExpressionChangedAfterItHasBeenCheckedErrorエラーについて知っておくべきことすべて

他に何もないとしても、このスレッドは公式のAngularドキュメントでこれらすべての詳細な警告をもたらすはずです。

@maximuskが共有した記事は必読です:ExpressionChangedAfterItHasBeenCheckedErrorエラーについて知っておく必要があるすべて。

ありがとうございました

@adamdport

@ViewChildren @ContentChildrenを使用して親から子プロパティを変更する場合、ngAfterViewInit()ライフサイクルが実行されるまで子は使用できないため、これは機能しますか?

この問題の一部は確認済みのバグであり、ユーザーの過失ではないことに注意してください。
https://github.com/angular/angular/issues/15634

@saverettが対処した問題は正しく、 *ngIfを使用するとエラーが発生していました。

*ウォッチング...

これは、オブジェクトをAngular Materialダイアログに渡し、ダイアログでthis.myDataInput = Object.assign({}, dialogDataInput)実行するのではなく、オブジェクトを直接使用する場合に発生する可能性があります。 つまり、 this.myDataInput = dialogDataInput;から、 this.myDataInput.isDefault = !this.myDataInput.isDefault;ようなことをします。 上で説明したように、子コンポーネント(ダイアログ)は、親コンポーネントのビューに表示される値を変更しています。

属性を[属性]に置き換えます

これが意図したエラーなのかバグなのか、誰かが確実に知っていますか?
ngOnInitにデータをロードするのは正しくありませんか? コードをコンストラクターに移動した場合(Observableを介してロードした場合)、エラーはスローされません...

@Wernerson 。 HTMLに* ngIfがありますか? ngOnInitが終了した後、物事がレンダリングされることに注意してください。長い間、* ngIf内のすべてが機能せず、このエラーが発生する可能性があります。

作り直さなければなりません。 「ngIf」とカスタムコントロールを使用すると、このエラーメッセージが表示されます。 タイムアウトは確かに解決策ではありません。

@ j-nordコメントを読んだら、 timeoutは必要ないことを知っているでしょう。

@ j-nord / @ dgroh私も同じことを経験しました。 ngIfディレクティブを使用するng-hintを使用してきましたが、このエラーを取り除くことができません。 回避策を実装できましたか?

@Wernersonはい、この問題の一部は意図されたエラーまたはバグです: https

@ mawo87 DOMから要素を削除しないため、非表示で機能しました。 私たちの要件では、これは問題なく、クリーンなソリューションでもありました。

@ mawo87
まず、エラーメッセージが意味する変数を見つける必要があります。 @Angular Team :エラーメッセージに変数の名前を

this.cdr.detectionChanges();

6月19日のvermilion928の例を参照してください

@dgrohと@ j-nordに感謝します。
@ j-nord私はdetectionChangesメソッドも調べていましたが、次の記事のように、このアプローチを使用しない可能性があります。
https://hackernoon.com/everything-you-need-to-know-about-the-expressionchangedafterithasbeencheckederror-error-e3fd9ce7dbb4

私の場合、PageTitleサービスを使用して親コンポーネントでサブスクライブし、子コンポーネントがビューに読み込まれると、サービスにPageTitleが設定され、親が値を取得してサブスクライブを介して表示します。 これは4.1.xではエラーなしで機能しましたが、4.2x以降ではExpressionChangedAfterItHasBeenCheckedErrorエラーがスローされます。

すべてが期待どおりに機能し、デプロイ済み/本番ビルドではエラーが表示されません(2番目の変更検出サイクルがないため)が、開発中はコンソールに非常に多くの赤が表示されます。

適切な(短期的な)修正が何であるかについては、まだ非常に不明です。 親コンポーネントのサブスクリプションはコンストラクターにあり、サービスを介した子コンポーネントの更新はngOnInitにあります。

私は「あなたが知る必要があるすべて...」の投稿を読みました。これは有益でしたが、私が何か間違ったことをしていると私に信じさせることはなく、問題に対する実用的な解決策を提供しません。

@alignsoft ngOnInitの子コンポーネントにページタイトルを設定する場合、エラーは有効だと思います...以前にリンクされた記事を参照してください。

@alignsoft同意します。このエラーについては多くの議論がありましたが、その有効性について明確な指示がありませんでした...これは標準とデータバインディング/操作に関する

私の特定のケースでは、 @ ViewChild@ContentChildを使用して親/子コンポーネントを処理しており、コンソールにエラーが表示されないように、detectChanges()を何度も呼び出す必要があります...

@rolandoldengarm親コンストラクターでサブスクライブし、子コンポーネントのAfterViewInitで値を設定しようとしましたが、それでもエラーが発生します。 これは、フローと変更の検出サイクルに基づいてこれを処理する正しい方法であり、エラーは潜在的にバグですか?

どうか、ここに何らかの公式の答えがあるといいですね...

これをもう一度見てください。 https://github.com/angular/angular/issues/17572#issuecomment-315096085からの私の例のように見え
コンポーネントのライフサイクルの一部としてContentChildrenを操作しようとするときは、 AfterViewInitではなくAfterContentInitを使用する必要がありました。

これがAfterContentInitでエラーのない更新されたplnkrです: http ://plnkr.co/edit/rkjTmqclHmqmpuwlD0Y9?p = Preview

@JSMike AfterViewInitAfterContentInit 、それでも同じエラーです。 OnInit内のデータを更新しても、エラーが発生します。 私の知る限り、これはOnInit間にデータを初期化するための正しいAngularライフサイクル(右?)によるものです。 このエラーが過去のバージョンでは発生しなかったという事実と合わせて、これはバグだと思います。 ただし、そうであれば、開発チームから何らかの発言をしたいと思います。 :D

@Wernersonデータがそのライフサイクルフックで利用可能でない限り、 ngOnInit内のデータを更新するべきではありません。 コンテンツの子に関係する場合は、ロジックをngAfterContentInit移動する必要があります。

@JSMike助けにはならなかった、それでもエラーは発生する。 ngAfterContentInitで要求されたデータを(非同期で)受信した後、アプリケーションの状態変更をサブスクライブします。

@Wernerson plnkrの例はありますか?

StackBlitzの基本サンプル
イベントを発行する子コンポーネントとローカル変数を更新する親コンポーネント。
jsコンソールでエラーが発生します:
ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'undefined'. Current value: 'hello world'.

ngAfterContentInitは、子が静的である場合にのみ機能し、 *ngFor http://plnkr.co/edit/rkjTmqclHmqmpuwlD0Y9?p = previewを使用して子を作成すると失敗します。

@soldiertt必要なのはdetectChanges() https://stackblitz.com/edit/angular-qfu74y

@JSMikeこれを行うと、子コンポーネントのngAfterViewInitがトリガーされません: https ://stackblitz.com/edit/angular-bdwmgf

@soldierttは奇妙に思えますが、報告する必要がある別の問題のように見えます。

@JSMike私のセットアップは、サービス、サードパーティライブラリ、機密情報などでかなり複雑です。残念ながら、プランカーでバグを再現できませんでした:/

ChangeDetectorRefの注入は私のために働きました!

カルーセルアイテムコンポーネントにいくつかのプロパティを設定する必要があるため、カルーセルでこの問題が発生します。このプロパティは、親カルーセルコンポーネントで計算された後にのみ認識されます。 また、すべてのアイテムはng-contentを経由するため、親カルーセルコンポーネントのngAfterContentInit内のアイテムのみを操作できます。 何かのようなもの:
カルーセル:

@ContentChildren(ItemComponent) carouselItems;

ngAfterContentInit() {
  // ... some computations
  this.carouselItems.forEach((item: ItemComponent, currentIndex) => {
    item.setPropertyX(someComputedValue);
    // OR:
    item.x = someComputedValue; // of course, this would work as well
  });
}

項目:

setPropertyX(value) {
  this.x = value;
}

setTimeout()ハックに加えて、他の唯一の解決策は、サービスとサブジェクトを使用して、アイテムを親カルーセルと通信させることでしょうか? それはばかげていることを意味します...つまり、カルーセルに100個のアイテムがある場合、100個のサブスクリプションがあります。 カルーセルが2つあると、2倍になります。 確かにそれはお勧めの方法ではありませんか?! 正しい?

多くの場合、コンポーネントにいくつかのプロパティを設定する必要があります。プロパティはAfterContentInitより前にはわかりません...そして多くの場合(次のような1つまたは2つのコンポーネントについて話していない場合)それではなく、カルーセルのように50または100)、10分の1または数百のサブスクリプションを持つのはばかげています。

私がまだ発見していない、ここで私を助けることができるAngularの本当にクールな部分はありますか?

PS私はまた、注入しようとしたChangeDetectorRef使いdetach()カルーセルコンポーネントと項目コンポーネントの両方に。 私はこれでエラーが修正されると思っていたでしょう...私は基本的に「_ネジの変更の検出、実際にはまったく必要ありません。どうぞ、変更を検出しないでください_」と言っています...しかし、エラーはまだスローされます! それで、それが言うことをしないならば、 detach()メソッドのポイントは何ですか?

constructor(private cdr: ChangeDetectorRef) {
  this.cdr.detach();
}

setTimeout()ハックする必要はありません。たとえば、ビューに反応して更新する場合など、非常に有効な方法である可能性があります。たとえば、 ngAfterViewInit中にテンプレートのバインディングを変更します。

問題は、理由を本当に知らずに魔法として使うときです。 しかし、 setTimeout()を使用することが適切な方法である完全に有効なシナリオがあります。

@ghetolay変更検出システムが評価しているコンポーネントのライフサイクルから

@fugwenna

変更検出システムが評価しているコンポーネントのライフサイクルから抜け出すのはハッキーではありませんか?

私はその質問を理解していません。 ライフサイクルから抜け出しますか?

Angularは、非同期イベント(domイベント、xhr、setTimeoutなど)が発生するたびにCDのラウンドを実行し、そのCD中にビューを更新します。 その後、 ngAfterViewInitフックが実行されるため、実行する予定のCDはなくなり、別の非同期イベントが発生するまで再度トリガーされることはありません。 setTimeout()が、それを再度トリガーする最も簡単な方法です。 あなたは確かにそれを自分で行うことができますが、私はその利点はごくわずかだと思います。

@ ghetolay @ JSMikeと私が読んだ他のいくつかのスレッドからのコメントを参照しています:

@matkokvesicは実際には解決策ではなく、ライフサイクルフックの外部で変更が発生するだけです。 提供されているコード例は、Angularv4.1.3を使用しているときにエラーを引き起こしません

したがって、setTimeout()を使用すると、ライフサイクルフックが「スキップ」されるか、通常のように評価されなくなると思いますか?

@ghetolay @fugwenna ChangeDetectorRefdetach()メソッドはどうですか? 何かご意見は? 変更検出を完全に無効にして、エラーを排除するべきではありませんか? なぜ動かないのか分かりませんか? それとも、スローされたエラーはそれとは「独立」していますか? Angularは、変更の検出を無効にしたという事実を実際には気にせず、プロパティが設定された後に変更したことだけを気にしますか?
「ExpressionChangedAfterItHasBeenChecked」がバグであるかどうかについて人々がまだ議論している場合、これは間違いなくバグである可能性があります...私の意見では、Angularは、プロパティが完全に設定された後、プロパティを変更することを気にする必要はありません。変更検出を無効にしました。 特に、親コンポーネントと他のすべての子コンポーネントについて、基本的にツリー全体で行った方法です。

私はここで少し迷っています。私が抱えている問題の1つは、開発モードで例外をトリガーせずに、非常に一般的なことをどのように行うかさえわからないことです。

たとえば、日付ピッカーをカプセル化する制御値アクセサーコンポーネントを作成しました。 このコンポーネントに空のモデルをフィードします。このモデルは内部でdatepickerに渡され、初期化され、登録された関数を介して返送されます。 もちろん、子供たちは初期化中にモデルを変更しますが、それが私が欲しいものです! 初期化されたモデルを取り戻したいのですが、その方法を理解しているのは日付ピッカーであるため、親で初期化を行いません。

問題は、私が今それをどのように行うかです。 setTimeoutを呼び出し、変更検出を再度トリガーしますか? ChangeDetectorRefを使用しますか? コードが本当に読みづらくなるので、私はそれが好きではありません。 別のサービスで物事を初期化しますか? しかし、そのサービスはビジネス上の価値がなく、非常に技術的な目的を果たしているだけであり、これで問題が解決するかどうかさえわかりません。

@fugwenna私の主な懸念は、4.1.3で機能し、現在は壊れていることでした。 init関数でsetTimeoutを使用し、ゾーンに引き継がせる必要があるかどうかについて、Angularチームのチャイムに参加している人はまだいませんが、以前の動作と比較すると、アンチパターンのように感じます。

@ kavi87したがって、このstackoverflowの質問は、実際に必要なエレガントなソリューションを提供してくれました。 これは、子コンポーネントのイベントエミッターを非同期にすることでした:
new EventEmitter(true)

109のコメントでリグレッションとしてラベル付けされた問題の場合、この問題は公式の言葉では残念ながら静かです。

ここの誰もがセルフヘルプテクニックで良い仕事をしていますが、これが実際にバグなのか、それとも4.2.x以降に実装された新しい動作なのか知りたいですか? もしそうなら、それは重大な変更としてリストされているはずです(したがってマイナーリリースではありませんか?)

@MrCroft
変更検出を完全に無効にして例外を無音にすることが、ダウンするのに適した方法かどうかはわかりませんが、理論的には、それがどのように意味をなすか、または意味があるかはわかります。

@JSMike@rosslaveryは、このエラーメッセージが4.2x以降に発生した理由について、変更を壊すという文書がないという点で、ほとんど応答や説明がないことに同意します...しかし、これのもう1つの不可解な部分は、どうやらそれがどのようにスローされるかです。開発モード。変更検出を中断するという警告のように感じます(?)

私のAppComponentにはdivがあります。
そのdivの唯一の目的は、ビジー状態のオーバーレイを表示することです。
ビジー状態は、httpサービスクラス内でトリガーされます。

app.component.html

<!-- Busy Overlay -->
<div *ngIf="busy$ | async"
     class="overlay">
    <i class="loading">
        <i><sk-wave></sk-wave></i>
    </i>
</div>

app.component.ts:

  get busy$(): Observable<boolean> {
    return this.catolService.busy$;
  }

4.3.4にアップグレードした後、サービスがビジー状態をトリガーすると、常にExpressionChangedAfterItHasBeenCheckedErrorが発生します。

この単純な非同期シナリオの修正はありますか?

@ emazv72 ...このstackblitz / plunkerのデモはあなたの場合と同じですか? https://stackblitz.com/edit/angular-2p2h9s?file=app%2Fapp.component.tsまたはhttps://plnkr.co/edit/RPOxSVnXvM1TqKp0peSY?p=preview

@mukunkuありがとう、やってみます。

@ mlc-mlapisプランカーを少し変更しました。コードは正確にhttps://plnkr.co/edit/uUT7tiM5BKPIy2upJPhb?p=previewです。

しかし、プランカーは問題を示していません。 最新の4.xにアップグレードして、修正されているかどうかを確認します。

ありがとう

みなさん、こんにちは。ここでの遅延についてお詫びします。

つまり、この動作は実際には意図したとおりに機能していますが、一部の人にとっては予想外のことかもしれません。そのため、メカニズムについて説明してみましょう。

- @saveretts「plunkerは(!ありがとう)を歩くためにこの良いREPROですhttps://plnkr.co/edit/fr7rTNTCgEhHcbKnxAWN?p=preview

tldrは、Angularが常にこのように機能していることです。変更検出は、1回のパスで上から下(または親->子)で実行されます。 したがって、次の場合:


@Component({ 
  selector: 'app-root',
  template: `
    <!-- initially falsey -->
    <div *ngIf="sharedService.someValue">NgIf Block</div>
    <child-comp></child-comp>
  `
})
class AppRoot {
  constructor(public sharedService:SharedService){}
}
@Component({ 
  selector: 'child-component',
  template: `child comp`
})
class ChildComponent {
  constructor(public sharedService:SharedService){}
  ngOnInit(){
    this.sharedService.someValue = true;
  }
}

変更検出は最初にapp-rootに対して実行され、そのすべてのバインディングをチェックしてから、子コンポーネントを変更検出します-これもシングルパスで実行するため(AngularJSスタイルが安定するまでチェックを続けるのではなく)、親バインディング(この場合は* ngIf)は再チェックされないため、アプリケーションは一貫性のない状態になります(!)

開発モードでは、実際にその変更検出を2回実行します。2回目の実行では、バインディングが変更されていないことを確認し(これは一貫性のない状態を示します)、そのような問題が見つかった場合はエラーをスローします。 この動作は、再現でenableProdMode()を呼び出すことで確認できます p

これが最近明らかになった理由は、以前は、コンポーネントを挿入するときにルーターがいくつかの追加の変更検出チェックを実行したためです。これは非効率的で、特にルーターのアウトレットがngIfsなどにネストされている場合にアニメーションとルーティングで問題が発生しましたこの動作が修正され、上記のようにすでに正しくないコードが発生し、開発者が一方向のデータフローに違反しているというエラーがれるようになりました

通常、この一方向のフローに違反するコード(上記の再現のように)は正しくないと主張します。 ただし、この動作が必要になる場合があります(実際、Angularフォームライブラリにも同様の設定があります)。 これが意味するのは、別の変更検出の実行を明示的にトリガーする必要があるということです(これにより、ルートコンポーネントに戻り、トップダウンでチェックされます)。 setTimeout呼び出すとこれが実現しますが、通常はPromiseを使用してこれをトリガーすることをお勧めします-https://plnkr.co/edit/IoKMxEOlY9lY9LcWwFKv?p = previewを参照して

お役に立てば幸いです。

@robwormaldこれと同じエラーが他のシナリオでも発生していました。 これでContentChildrenの問題が解決されるかどうかはわかりません。 ディレクティブがAfterContentInit中に子の値をエラーなしで変更できるのに、コンポーネントで同じことを行うとエラーがスローされるのはなぜですか?

また、1つのinitプロセス中にcdr.detectChanges()が呼び出された場合、他のinit関数が呼び出されなくなることが予想されますか? これは、このスレッド、別の問題を作成しました

@robwormald

@JSMikeは、ContentChild(ren)とViewChild(ren)を使用して親->子から一方向のフローが発生するシナリオでもこのエラーがスローされるという評価では正しいと思います。

私が以前か?つまり、このエラーは、開発者が一方向のフローの「ベストプラクティス」に反対しているが、変更の検出以降、アプリケーションの中断エラーを引き起こしていないことを示す「警告」です。本番モード(実世界)で1回だけ実行されますか?

@fugwennaいいえ、これは実際にはエラーです。そうしないと、ビューが一貫性のない状態のままになります。 Robが実行しているサンプルは、すべてを物語っています。

また、ドキュメントからのこの引用があります

Angularの単方向データフロールールは、ビューが作成された後のビューの更新を禁止します。 これらのフックは両方とも、コンポーネントのビューが作成された後に起動します。

そこで本当に何かを変更する必要がある場合は、非同期メソッドの1つを使用して変更します。 setTimeoutが適しているかもしれませんが、(この記事の執筆時点では)4ミリ秒の遅延が伴うことに注意してください。 別の方法はPromise.resolve().then(() => Assignment = here)

さらに良いのは、一歩下がって、プロセスの後半で変更を加える必要がある理由を確認し、問題を解決するためのよりクリーンな方法があるかどうかを確認することです。 私がこれをしていることに気付いた場合、私はそれをコードの臭いとして扱います。

詳細な説明@robwormaldをありがとうございます。 *ngIfではなく[hidden]バインディングを使用すると、子->親データフローが引き続き機能するように見える理由がまだわかりExpressionChangedAfterItHasBeenCheckedErrorを与えなくてもこれがまだ機能する理由

AngularはcheckAndUpdateView関数を使用して変更検出を実行します。

この関数の短い表現は次のようになります。

function checkAndUpdateView(view) {
    // update bound properties and run ngOnChanges/ngOnInit/ngDoCheck for child directives
    updateDirectives
    // check all embedded views that belong current view
    execEmbeddedViewsAction(view, ViewAction.CheckAndUpdate);
    // update DOM for the current view
    updateRenderer
    // perform checkAndUpdate for all child components
    execComponentViewsAction(view, ViewAction.CheckAndUpdate);
}

execEmbeddedViewsAction execComponentViewsActionメソッドと

@saverettの例を単純化するために、次のテンプレートを使用します

<div *ngIf="!sharedService.displayList">Some text</div>
<router-outlet></router-outlet>

https://plnkr.co/edit/OdkzHjEXbEd3efYAisFM?p=preview

*ngIfが単なる脱糖であることはすでに知っています。 そう

<ng-template [ngIf]="sharedService.displayList">
  <div>Some text</div>
</ng-template>
<router-outlet></router-outlet>

次の構造を想像することができます。

  my-app
     |
   NgIf directive
       NgIf EmbeddedView
   RouterOutlet directive

RouterOutletディレクティブは何をしますか?

ルーティングされたコンポーネントをViewContainerRefに挿入します

activateWith(activatedRoute: ActivatedRoute, resolver: ComponentFactoryResolver|null) {
    ...
    this.activated = this.location.createComponent(factory, this.location.length, injector);

これは、 router-outletEmbeddedView router-outlet作成することを意味します。したがって、 RouterOutletディレクティブが初期化された後、 AppComponentビューに2つの埋め込みビューがあります。

  my-app
     |
   NgIf directive ([ngIf]="sharedService.displayList")
       NgIf EmbeddedView
   RouterOutlet directive 
      BComponent EmbeddedView

写真で説明しましょう

haschanged

それでは、メイン機能に戻りましょう

function checkAndUpdateView(view) {
    // update bound properties and run ngOnChanges/ngOnInit/ngDoCheck for child directives
    updateDirectives
    // check all embedded views that belong current view
    execEmbeddedViewsAction(view, ViewAction.CheckAndUpdate);
    ...
    // update DOM for the current view
    updateRenderer
    ...
}

Angularは次の順序でチェックを実行します。

NgIf directive ([ngIf]="sharedService.displayList") updateDirectives(1)
      NgIf EmbeddedView   execEmbeddedViewsAction(2)
RouterOutlet directive 
      BComponent EmbeddedView  execEmbeddedViewsAction(3)

NgIfディレクティブが最初にチェックされ(ここではsharedService.displayList=falseが記憶されます)、次にBComponent内のNgOnInitメソッドが呼び出されます(ここでsharedService.displayList変更します)。 true )そしてangularがcheckNoChangesすると、エラーExpression has changed after it was checked発生します

次に、 *ngIf[hidden]置き換えましょう。 これで、 RouterOutletによって挿入された埋め込みビューが1つだけになり、 [hidden]="sharedService.displayList"updateRenderer内でチェックされます。

  my-app
     |
   div{Some Text} just a node
   RouterOutlet directive 
      BComponent EmbeddedView

Angularは次のようなチェックを実行します:

function checkAndUpdateView(view) {
    // check all embedded views that belong current view
    execEmbeddedViewsAction(view, ViewAction.CheckAndUpdate);   BComponent.ngOnInit (1)
    ...
    // update DOM for the current view
    updateRenderer   update hidden binding (2)
    ...
}

このように、 ngOnInit値を呼び出した後に更新したため、domプロパティのバインドによってエラーが発生することはありません。

これが、構造ディレクティブ*ngIfとdomプロパティバインディング[hidden]の主な違いです。

詳細な概要@alexzuzaをありがとう! とても助かりました!

setTimeout()は実際に私のために働いた:)ありがとう@jingglang :)
.subscribe(data => setTimeout(()=> this.requesting = data、0))

@robwormaldはここで正しいです。 同じ問題があり、NGRXとオブザーバブルを使用していたため、検出がより困難でした。 私が抱えていた問題は、ストアの状態にサブスクライブしているルート階層のさまざまなレベルにコンポーネントコンテナがあることでした。 コンテナコンポーネントを機能モジュールの同じレベルに移動するとすぐに、問題は解決しました。 これは一種の複雑な答えだと思いますが、NGRXコンテナ/コンポーネントパターンを使用している場合は、これが正しい方向への調査を示していることを願っています

setTimeOutで動作します、ありがとう@jingglang
ngAfterViewInit(){
this.innerHeight =(window.innerHeight);
setTimeout(()=> this.printPosition()、0);
}

@alexzuza 、これはngIfだけでは発生しません、これは(少なくとも私にとっては)[ngClass]でも発生します

@ Ninja-Coding-Git特定のケースについて説明しました。 私はそれがどんなコードでも起こり得ることを知っています。

私の場合、要素を変更した後にChangeDetectorRefを呼び出すと、問題が解決しました。

import { Component, ChangeDetectorRef } from '@angular/core'

@Component({
  selector: 'my-app',
  template: `<div>I'm {{message}} </div>`,
})
export class App {
  message: string = 'loading :(';

  constructor(private _cdr: ChangeDetectorRef) {}

  ngAfterViewInit() {
    this.message = 'all done loading :)'
    this._cdr.detectChanges();
  }

}

ここで解決策が見つかりまし

こんにちは@thetylerb! これについて詳しく教えていただけますか。 私たちはngrxを使用していますが、これを補正する方法がわかりません。 オブザーバブルの上にプロミスを使用するのはかなり厄介だと思います。

@ piq9117このコメントもご覧くださいhttps://github.com/angular/angular/issues/18129#issuecomment-326801192変更検出とngrxの相互作用について作成しましたが、役立つと思います。

@victornoelありがとう! それは私たちの状況をほぼ説明しています。 ネストされた不変のデータ構造(immutablejs)を取得し、あらゆる場所で非同期パイプを使用しています。 私の最初の考えは、ストアがハイドレイトされたときにストアがコンポーネントツリーを変更し、コンポーネントツリーがこの変更を補正して別のchangeDetectionを実行するというものでした😓

ネストされた不変のデータ構造(immutablejs)を取得し、あらゆる場所で非同期パイプを使用しています。 私の最初の考えは、ストアがハイドレイトされたときにストアがコンポーネントツリーを変更し、コンポーネントツリーがこの変更を補正して別のchangeDetectionを実行するというものでした。

私のプロジェクトでは、(ネストされたデータ構造の代わりに)ある種の正規化されたデータ構造があるので、そうならないまで、この種の問題を少し軽減します! :O

あなただけが望むのは:

  • ある部分への変更が他の部分への更新をトリガーしないようにデータ構造を保存するように変更する
  • 非同期パイプを介してストアをリッスンするコンポーネントの子からイベントをトリガーしないように注意する
  • 前述のイベントをトリガーすると、手動で変更検出をトリガーします
  • これらのイベントを別の時間またはsetTimeoutでトリガーします

私はngrxを使用していて、次のような状況になりました。

app.component.htmlの場合:
<div *ngIf='(appState | async).show'>test</div>
そしてapp.component.tsで
this.appState= this.store.select("app");

もちろん、この状況ではエラーが発生しますが、次のようなコンポーネントにブールフラグを追加すると、次のようになります。
app.component.html-> <div *ngIf='show'>test</div>
app.component.ts->
this.appState.subscribe((state: AppState) => { this.show= state.show; })

エラーはなくなりました。もちろん理由は理解できますが、あなたの意見を知りたいのですが、それは良い解決策ですか? 長所と短所は何ですか?

ngrxを使用していませんが、querySelectorAll let chips = [].map.call(document.querySelectorAll('li.chip'), this.chipVisible);を使用した簡単なチェックで問題が発生しています

ChangeDetectorRef.detectChanges()を使用した@bogdancarのソリューションはエラーを抑制しますが、最初にエラーの原因となっているものを修正してChangeDetectorRefをインポートする必要がないのは良いことです。

Angular4.4.4でrxjsを使用したときに同じ問題が発生しました。 DIサービスが提供する監視可能なオブジェクトをサブスクライブするだけです。 以下の私の回避策:

this.eventBroadcastService.getCustomEvent({type: 'loginOpen'}).subscribe(e => { setTimeout(() => { this.isBlur = true; }, 0); });

setTimeoutを使用して回避します。 今では動作しますが、注意が必要です。 気分が悪い。

ngOnChanges(changes:SimpleChanges | any)はトリックを行います

この問題は、ngDoCheck()メソッド内で動的に変更されるパラメーターロジックを移動した直後に修正されました。
ngDoCheck(): void { this.checkValid = this.sourceArr.includes((this.chanedVal).toUpperCase()); }

@ asrinivas61これは本当に悪い考えだと思います。 ngDoCheckはAngularから頻繁に呼び出されており、 sourceArrに大量のデータがあると、パフォーマンスが低下する可能性があります。

https://angular.io/guide/lifecycle-hooks#other-angular-lifecycle-hooksを参照して

今のところ、 ngAfterViewInitsetTimeout ngAfterViewInitを使用することをお勧めします:

setTimeout(_ => /* your code here */, 1000);

例:

  public ngAfterViewInit(): void {
    // We use setTimeout to avoid the `ExpressionChangedAfterItHasBeenCheckedError`
    // See: https://github.com/angular/angular/issues/6005
    setTimeout(_ => this.isLoadingInProgress = false, 1000);
  }

@dgroh実際のタイムアウトを追加する必要はありません。次の非同期タスクでコードを実行するだけで、別の変更検出が実行されます。

@victornoel ChangeDetectorRef detectChangesを明示的に呼び出さない限り、これはオブザーバブルで機能すると私は信じています。 これがすべて必要なかったらいいのにと思います。

@dgrohあなたは私を誤解したに違いありません、私はただ書くことを提唱していました:

public ngAfterViewInit(): void {
    // We use setTimeout to avoid the `ExpressionChangedAfterItHasBeenCheckedError`
    // See: https://github.com/angular/angular/issues/6005
    setTimeout(_ => this.isLoadingInProgress = false);
  }

後にステップを追加することはできませんngAfterViewInitのようにsetTimeout ? (例: ngAfterViewLoad

@ aks4it 、これは機能しているようですが、どのように機能するかについて詳しく知りたいですか? 動的コンポーネント生成コードは同期していますか?

Angularドキュメントの公式プランカーデモにも同じエラーがあります。 そこで修正してもらえますか? :)ありがとう

@luckylooke

  ngAfterViewInit() {
    //this.loadComponent();
    this.getAds();
  }

これは動作しますか?

私はあなたの要件を理解していませんが、あなたは本当に3秒の間隔が欲しいですか? はいの場合、これは機能します。loadComponentを直接呼び出したい場合でも、次のように成功しました。

ngAfterViewInit() {
    setTimeout(()=>{this.loadComponent()},0)
  }

@istiti ChangeDetectorRef回避策で修正でき、... data = dataの後にdetectChanges()

しかし、それは公式のドキュメントからのプランカーなので、私は問題の公式の解決策に興味がありました。 タイムアウトまたはChangeDetectorRefではありません。

申し訳ありませんが、最初の投稿では短すぎました。角度のあるドキュメントでもコードが同じであるため、問題の重大度を指摘したいと思いました。

参考までに、ここで実際に何が発生しているのかわかりませんが、main.tsでprodモードを有効にするとエラーが発生しなくなります
`enableProdMode()

// if(environment.production){

// enableProdMode();

//} `

"@ angular / core": "^ 4.2.4"
"typescript": "〜2.3.3"

@bakthaa ...各CDサイクルの後に呼び出される制御チェック(コンポーネント@Inputs()値が同じかどうか)は、Angularアプリの本番モードではなく、開発モードでのみ呼び出されるため、動作が想定されます。 したがって、問題は本番モードでは隠されていますが、それはまだ存在しています...そしておそらく本番モードでのさまざまな望ましくないまたは誤解を招く副作用の原因となる可能性があります。

Angular 5を使用していますが、コンポーネントにChangeDetectorRefを挿入し、変更を強制的に検出することで、この問題を修正できました。

    constructor(private changeDetector: ChangeDetectorRef) {}

    ngAfterViewChecked(){
      this.changeDetector.detectChanges();
    }

@ Sabri-Bouchlema ...はい、それは1つの方法です...しかし、それでももう一度考え直す必要があります...コンポーネント@Inputsが1つのCDサイクル中に変更される理由...そしてロジックは正しいです...そして、95%のケースから、追加のdetectChanges()を呼び出さなくても、問題を回避するためにコードを変更できることは事実です。

角度5.1.2を使用します。 ngAfterViewCheckedでマテリアルダイアログを開くと、このエラーが発生します。 @ aks4itが報告したように、それを機能させるには、コードをsetTimeoutにラップする必要がありました。

バージョン4.01でも同じ問題に直面しています
ng-version = "4.0.1

ここでも角度4.4.6で同じです

this.changeDetector.detectChanges();を使用して修正

テンプレート変数を使用してこのプランカーを作成しましたが、ExpressionChangedAfterItHasBeenCheckedErrorがある理由がわかりません。 コンポーネントの順序を変更すると、エラーが表示されなくなります。

https://plnkr.co/edit/Mc0UkTR2loI7wgmLAWtX

changeDetector.detectChangesを試しましたが、機能しません

@AlexCenteno ...評価ロジックは、上から下にある...およびのでvalueのプロパティcomp1設定されている@Input() public value:String=null;第一及びその後の再セットの基準を使用してcomp2.value ... <div comp1 [value]="comp2.value"></div>は、独自の@Input()を介してHello設定されます...すべて1枚のCDサイクルで...繰り返しチェックします子コンポーネントの入力が開発モードのCDサイクルの開始時と同じである場合、 comp1 valueプロパティの変更を検出します。

こんにちは@ mlc-mlapisお時間をいただきありがとうございます。 それで、エラーを回避する方法はありませんか? enableProdModeを呼び出したら、心配する必要はないのではないでしょうか。

@AlexCenteno ...はい、prodモードでは、CDサイクル後のチェックは呼び出されません...しかし、valueクロスチャイルドコンポーネントを渡す別のロジックを適用する必要があります。 <div>プロパティvalueがないという問題もあります。 なぜ属性コンポーネントを使用するのですか? なぜだけではないのですか?

<comp1 [value]="comp2.value"></comp1>
<comp2 #comp2 value="Hello"></comp2>

その後、使用しているため@Output()<comp2>罰金になり、それはあなたが更新できるようになる[value]comp1エラーなし。

angular@5標準store.select [email protected]と、開発モードで警告ExpressionChangedAfterItHasBeenCheckedErrorがスローされ、本番モードで正しく機能しません。 ディスパッチと選択は、さまざまなレベル/コンテナで行われます。

@Component({
  ...
  template `
  ...
      <div [ngClass]="{
        'home__sidenav': true,
        'home__sidenav--active': (isActiveSidenavMenu$ | async)
      }">
  ...
  `
})
export class HomeContainer {
  isActiveSidenavMenu$ = this.store.select(fromHome.isActiveSidenavMenu);
  ...
}

このアプローチは警告メッセージを削除しますが、より複雑なようです。

@Component({
  ...
  template `
  ...
      <div [ngClass]="{
        'home__sidenav': true,
        'home__sidenav--active': isActiveSidenavMenu
      }">
  ...
  `
})
export class HomeContainer {
  isActiveSidenavMenu$ = this.store.select(fromHome.isActiveSidenavMenu);
  isActiveSidenavMenu;

  ngOnInit() {
    this.isActiveSidenavMenu$.subscribe(res => {
      this.isActiveSidenavMenu = res;
      this.cd.detectChanges();
    });
  }
}

この問題をより正確に解決する方法はありますか?

@rimlinストアに直接登録してみましたか?

ngOnInit() {
  this.store.select(fromHome.isActiveSidenavMenu)
    .subsbrice(res => {
      this.isActiveSidenavMenu = res;
    });
}

同様の問題があったと思います。 テンプレートでasyncを回避することで、問題が解決しました。

@ piq9117アドバイスをありがとう、あなたの解決策を試しました、それはより少ないコードでより明確に見えます、しかしここでもthis.cd.detectChanges();を呼び出す必要があります、さもなければビューは更新されません。

ngOnInit() {
    this.store.select(fromHome.isActiveSidenavMenu).subscribe(res => {
      this.isActiveSidenavMenu = res;
      this.cd.detectChanges();
    });
}

私はAngular5.2を使用しています、私はそのようなことをしました。 私のために働く

constructor(private changeDetector: ChangeDetectorRef) {}

ngAfterViewInit(){
  this.changeDetector.detectChanges();
}

Angular 5.2の使用、detectChanges()を使用して修正

はい、detectChanges()ルートを使用してこれを修正しました(Angular5.1)。 私はここに他の多くの人と一緒にいますが、これはまだ単なる回避策であり、長期的には答えとして扱われるべきではないと考えています。 使用できるようにするには、別のライフサイクルフックが必要です。

このような変更を許可するためにまったく新しい変更検出サイクルをトリガーすることは、最適ではないように思われます。

すべての回答とアドバイスがすでに上に投稿されているにもかかわらず、これは継続しているようです。

  1. これを修正する正しい方法は、コンポーネントとデータ処理を再構築/再構築して、トップダウンで1回の変更検出パスを処理することです。 これは、子コンポーネントが親コンポーネントのテンプレートで使用される値を変更できないことを意味します。 構築したものを再評価し、データがコンポーネントツリーを下って一方向にのみ流れることを確認します。 これは、コードをngAfterViewInitからngOnInitに移動するのと同じくらい簡単な場合があります。
  2. this.changeDetectorRef.detectChanges();は、最初のステップで問題が発生する場合の5%未満で、これを処理するための公式で認められた方法です。

その他の注意事項:

  1. ngAfterViewInit何かを行うときは、DOMで使用される値を変更するとこのエラーが発生する可能性があるため、十分に注意してください。
  2. CheckまたはCheckedライフサイクルフックのいずれかを使用する場合は、変更検出サイクルごとに呼び出される可能性があるため、十分に注意してください。
  3. setTimeoutはこれを回避できますが、上記の2つのソリューションが推奨されます。
  4. 開発モードでこのエラーを無視し、本番モードでエラーが「修正」されることに満足している場合は、上記で説明したように無効です。 これらのチェックはprodモードでは行われませんが、prodモードのコードは変更を検出せず、ビューとデータの同期をとっていません。 これは場合によっては無害ですが、無視するとデータ/ビューの破損やさらに悪い問題につながる可能性があります。
  5. 一方向のデータフローはAngularであまり変更されていないため、バージョンXのAngularでこれにヒットしたことを投稿してもあまり役に立ちません。
  6. ライフサイクルフックまたは状態を追加することは、これに対する解決策ではありません。 開発者は、これらのフックへの一方向のデータフローに違反するコードを追加するだけで、これと同じエラーが発生します。

最後に、このエラーはAngularの問題を示しているわけではないことを覚えておいてください。 このエラーは、コードで何か問題が発生したこと、およびAngularを高速かつ効率的にする一方向のデータフローシステムに違反していることを通知するためにここにあります。

上記の投稿と多くのブログ投稿はこれをより深く掘り下げており、この一方向のデータフローモデルと、コンポーネントを構築してその中にデータを渡す方法を理解する必要があるため、読む価値があります。

@Splaktar上記のコメントを

@Splaktarこの点を「これは、子コンポーネントが親コンポーネントのテンプレートで使用される値を変更できないことを意味します」と

ナビゲーションやページタイトルなどを含むコンテナコンポーネントがあり、それぞれが独自のサブコンポーネントをロードする可能性のあるサブコンポーネントのコレクションをロードするとします。 コンテナにロードされるコンポーネントはデータに基づいて変更され、それらがロードするサブコンポーネントはデータに基づいて変更されます。 これらの各サブコンポーネントは、親コンテナが適切なトップレベルのナビゲーションをユーザーに表示できるように(おそらくパンくずリスト、または同様のもの)、それらが何であるかに関する情報を親コンテナに通信できる必要があります。コンテナレベルでビューにロードされているものを識別するためにタイトルで使用できる情報として。

これは私の経験では非常に一般的なアーキテクチャであり、サブコンポーネントが登録する共通の状態サービスを利用し、親コンポーネントがサブスクライブして、子供が親と親を教育できるようにします。適切な情報を表示できます。

親はサービスからすべての関連データを取得し、一方向通信を介して子コンポーネントに必要な情報をプッシュダウンするため、これはボトムアップアプローチに従っていません。

これはあなたが上で提案していることに反しているようで、私にとっては問題の原因です。 子コンポーネントがサービスに登録されると、親コンポーネントはエラーをスローします。

これは間違っていますか?

そして、承認されたバグがあることを決して忘れないでください:
https://github.com/angular/angular/issues/15634

@ Martin-Wegnerhttps //github.com/angular/angular/pull/18352#issuecomment-354170352を参照して

@alignsoft ...あなたのケースと問題をStackblitzとして簡単に再現していますか? あなたが説明したロジックだけでなく、何か他のものがケースに影響を与えるはずだからです。

@Splaktarからの解決策はここで私の簡単な問題に関連していない@trotyl https://github.com/angular/angular/issues/17572#issuecomment -311589290か?

@ Martin-Wegner ...あなたの場合、 ngAfterViewInitngOnInitフックに変更するだけで十分です。

@ alignsoft 、@ mlc-mlapisが言ったように、Stackblitzの例は、この場合の適切なアーキテクチャを示すのに非常に役立ちます。 これは、適切なサービスセット、オブザーバブル、ライフサイクルフックなどを使用して非常に可能です。この特定のケースは、StackOverflowにとっても良いトピックです。

@Splaktar現在の締め切りが机から離れたら、簡単なケースを示すプロジェクトをモックアップします。

@ mlc-mlapisすごいありがとう、それは動作します:)

@alignsoft 、今日私はあなたが説明したのとまったく同じように動作するアプリを作成していました。 @ MLC-mlapisは、あなたは上の例でストリップダウン見つけることができますstackblitz 。 左側のナビゲーションで[CMSシステム]をクリックすると、 ExpressionChangedAfterItHasBeenCheckedError例外がスローされます。

この例では、admin.componentは、次の3つのことを担当する親コンポーネントとして機能します。

  • 左ナビゲーションを表示する
  • サブコンポーネントが独自のティッカーメッセージを表示するために使用できるトップティッカー
  • <router-outlet></router-outlet>介してサブコンポーネント固有のデータを表示するメインコンテンツセクション。

親コンポーネント

<div class="ticker-container" *ngIf="tickedMessage !== ''">
  <h1>{{tickedMessage}}</h1>
  <button type="button" (click)="tickedMessage = ''">
    close
  </button>
</div>

<div class="side-nav">
  <h2>side nav</h2>
  <ul>
    <li><a routerLink="/">dashboard</a></li>
    <li><a routerLink="/apps/thirdpartycms">CMS System</a></li>
  </ul>
</div>
<div class="main-content-container">
  <router-outlet></router-outlet>
</div>
export class AdminComponent implements OnInit, OnDestroy {
  private _tickedMessageSubscription: Subscription;

  tickedMessage: string = '';

  constructor(
    private router: Router,
    private adminService: AdminService
  ) { }

  ngOnInit() {
    /* 
      On the side nav if you click on CMS System, this piece of code throws the exception: 
      "ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed 
       after it was checked. Previous value: 'false'. Current value: 'true'.""
    */
    this._tickedMessageSubscription = this.adminService.tickedMessageAnnounced$.subscribe((tickedMessage: string) => {
      this.tickedMessage = tickedMessage;
    });
  }

  ngOnDestroy() {
    this._tickedMessageSubscription.unsubscribe();
  }
}

データを通信する方法を研究に使用した場合、親コンポーネントにバックアップ<router-outlet></router-outlet> 、私は私が使用している場合ことを読んで@Output親コンポーネントへのデータバックを送信するために、ルータ・アウトレットディレクティブが終わるだろうイベントの処理。 これはカスタムイベントであるため、router-outletディレクティブはそれを処理する方法を知りません。 そのため、この方法はスキップしました。 angle.ioで共有サービスについて読んだら、私は自分で書いた:

共有サービス

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

@Injectable()
export class AdminService {

  private _tickedMessageAnnounced = new Subject<string>();

  tickedMessageAnnounced$ = this._tickedMessageAnnounced.asObservable();

  announceTickedMessage(tickedMessage: string) {
    this._tickedMessageAnnounced.next(tickedMessage);
  }
}

this.adminService.announceTickedMessage(this._tickedMessage);と呼ばれる子コンポーネントの1つ:

子コンポーネント

import { Observable } from 'rxjs/Observable';
import { Component, ViewChild, OnInit, Inject } from '@angular/core';
import { AdminService } from '../../../core/admin/admin.service';

@Component({
  selector: 'cms',
  templateUrl: './thirdparty-cms.component.html',
  styleUrls: ['./thirdparty-cms.component.scss'],
})
export class ThirdPartyCmsComponent implements OnInit {
  private _tickedMessage: string;
  constructor(private adminService: AdminService) { }

  ngOnInit(): void {
    this._tickedMessage = 'Please do not create an entry for a page URL that does not exist in the CMS system';

    this.adminService.announceTickedMessage(this._tickedMessage);
  }
}

親コンポーネントは、子コンポーネントによって送信されたチェックされたメッセージ値でパブリックプロパティの1つを更新しようとしたため、 ExpressionChangedAfterItHasBeenCheckedError例外をスローしました。

親コンポーネント(例外をスローします)

export class AdminComponent implements OnInit, OnDestroy {
  private _tickedMessageSubscription: Subscription;

  tickedMessage: string = '';

  constructor(
    private router: Router,
    private adminService: AdminService
  ) { }

  ngOnInit() {
    /* 
      On the side nav if you click on CMS System, this piece of code throws the exception: 
      "ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed 
       after it was checked. Previous value: 'false'. Current value: 'true'.""
    */
    this._tickedMessageSubscription = this.adminService.tickedMessageAnnounced$.subscribe((tickedMessage: string) => {
      this.tickedMessage = tickedMessage;
    });
  }

  ngOnDestroy() {
    this._tickedMessageSubscription.unsubscribe();
  }
}

ExpressionChangedAfterItHasBeenCheckedErrorを修正したのは、サブスクライブコールバックを非同期で実行し、チェックされたメッセージを待つこと

親コンポーネント(非同期待機あり)

export class AdminComponent implements OnInit, OnDestroy {
  private _tickedMessageSubscription: Subscription;

  tickedMessage: string = '';

  constructor(
    private router: Router,
    private adminService: AdminService
  ) { }

  ngOnInit() {
    /* 
      This clears the ExpressionChangedAfterItHasBeenCheckedError exception.
    */
    this._tickedMessageSubscription = this.adminService.tickedMessageAnnounced$.subscribe(async (tickedMessage: string) => {
      this.tickedMessage = await tickedMessage;
    });
  }

  ngOnDestroy() {
    this._tickedMessageSubscription.unsubscribe();
  }
}

WPFの世界から来て、オブザーバブルは非同期で実行されているので、UIスレッドにオブザーバブルサブスクリプションコールバックを待つように指示する必要があるようです。

私はAngularを始めたばかりなので、Angularスキルに思いやりを持ってください:P

@ktabarezありがとう、これは私を大いに助けました!

@ktabarezありがとう! それはうまくいくようです! サブスクライブを親コンポーネントでタイムアウトでラップしていました。これも機能していましたが、汚れているように感じました。

redux状態を問い合わせると、確実に配信されることがわかりますが、起動時には、コンストラクターではなくngOnInitに配置されます。

@ktabarezどうもありがとう!

async ... await in subscribeソリューションが機能します。

@ktabarezあなたのソリューションは基本的にsetTimeout({}、0)と同じことをしていますが、よりエレガントに感じます。 とても気に入っています! また、何でも待つことができることに気づきました(C#await Task.FromResult(tickedMessage)とほぼ同じです)。

それで、質問:頻繁に変更される可能性のあるバックエンドからデータを取得しているオブザーバブルのプロパティの計算で非同期/待機を実行する必要があるのはなぜですか? ngClassはこれを必要としますか? {observable |を使用しているときにこれを取得します非同期}パイプ。 したがって、async / awaitは私にとって最良のアプローチではありません。 (または非同期パイプは最善のアプローチではありません)

親コンポーネントに複数の子コンポーネントがある場合、この問題が発生します。 子コンポーネントの1つには、親コンポーネントプロパティからのデータが必要な入力があり、プロパティはObservable / Promiseからデータを取得します(両方を試しました)。

parent.component.html

<mat-tab-group>
   <mat-tab>
      <app-child-1></app-child-1>
   </mat-tab>
   <mat-tab>
      <app-child-2></app-child-2>
   </mat-tab>
   <mat-tab>
      <app-child-3 [datasource]="data"></app-child-3>
   </mat-tab>
</mat-tab-group>

parent.component.ts

// ... omitted...
data: any;

ngOnInit() {
   this.service1.getMethod(some_id).then(data => this.data = data);
}

私はスレッドで見つかったすべての解決策に出くわしました。 しかし、私の場合は何も機能しません。 幸いなことに、これは、例として_(ngIf)_に式を配置するなど、DOMの操作による問題であることが示唆されました。

したがって、これを回避するために、 ng-templateng-container 、およびngTemplateOutletを試してみます。

<ng-container *ngTemplateOutlet="data ? content : loading"></ng-container>

<ng-template #content>
   <mat-tab-group>
      <mat-tab>
         <app-child-1></app-child-1>
      </mat-tab>
      <mat-tab>
         <app-child-2></app-child-2>
      </mat-tab>
      <mat-tab>
         <app-child-3 [datasource]="data"></app-child-3>
      </mat-tab>
   </mat-tab-group>
</ng-template>

<ng-template #loading>Loading your component. Please wait</ng-template>

ChangeDetectorRefまたはsetTimeout()を使用せずにこれを解決することができました

私にとって、このエラーは条件に応じてフィールドを表示/非表示にするときに発生しました、フィールド設定の値を適用した後にそれを解決することができました:

this.form.get ('field').updateValueAndValidity();

私はinitで開くmat-dialogでこれを実行しようとしており、このスレッドで見たすべてのソリューションを文字通り試しました(set timeout、promise resolve、observable pipe、after view init、after content init)およびその中の組み合わせ。 それでも動作しません。 次に、これが一方向のデータフローにどのように違反するかについて、 @ Splaktarの投稿を見たので、文字通りngrxにフラグを作成してダイアログを開き、それをサブスクライブしました。これは最初はfalseで、まだこのエラーが発生します。 一体何をしますか? 完全に別のイベントにアタッチせずにモーダルダイアログを開く方法は文字通りありませんか?

init:
this.store.pipe( select(getShouldLogin), ).subscribe((shouldLogin: boolean) => { if (shouldLogin) { this.openLoginDialog(); } }); this.checkUserId();

補助機能:
checkUserId() { const id = this.cookies.getUserId(); if (!id || id === 'undefined') { this.connApi.setShouldLogin(true); } }
openLoginDialog() { const dialogRef = this.dialog.open(LoginDialogComponent, { width: '400px', height: '300px', }); dialogRef.afterClosed().subscribe(result => this.handleLogin(result)); }

角度6.0.0-rc.1を使用

私はngrxでangularを使用していますが、フォームを送信した後にこのエラーが発生しました。
設定により修正
ChangeDetectionStrategy.OnPush
フォームコンポーネント上

まだ問題があります。 あらゆる種類の回避策を試しました。 setTimeoutは最初は機能しますが、その後dialog.openトリガーすると、同じエラーが発生します。 これに関するステータスはありますか?

私は同じ問題に直面しています。 すべてを試しましたが、この問題は一方向のデータフローで発生しているようです。
ここで私はselectedwoidsを更新しようとしています。 問題は解決しますが、同じエラーが発生します。

        <p-row> <p-column class="text-left" footer="{{selectedWoids.length}} {{getWoidString(selectedWoids.length)}} selected out of {{getTotalCounts()}} {{getWoidString(getTotalCounts())}}" colspan="11"></p-column> </p-row>

プライベートsetSelectedWoidsCount(){
if(this.isSelectAllWoids){
this.selectedWoids = this.allWoids;
}
}

Angularのレンダリングパイプラインを壊したため...

4.1.2から4.2.3に切り替えた後、同様の「ExpressionChanged ...」エラーが表示されます。

私の場合、サービスを介して相互作用する2つのコンポーネントがあります。 このサービスは私のメインモジュールを介して提供され、 ComponentAComponentB前に作成されます。

4.1.2では、次の構文はエラーなしで問題なく機能しました。

_ComponentAテンプレート:_

<ul *ngIf="myService.showList">
...

_ComponentBクラス:_

ngOnInit() {
  this.myService.showList = false; //starts as true in the service
  ...
}

4.2.3では、「ExpressionChanged ...」エラーを回避するために、 ComponentAのテンプレートを次のように変更する必要があります。

_ComponentAテンプレート:_

<ul [hidden]="!myService.showList">
...

なぜこれが問題になっているのか、そしてなぜ*ngIfから[hidden]への切り替えが機能するのかを理解しようとしています。

ngIfから[hidden]に切り替えたときにどのように機能するかわかりましたか

Angular 6でこの問題の解決策はまだありますか?

@alokkarma ...古すぎます...

同じケースがバージョン4.1.2で機能していたという事実は、4.2.3を含む後のバージョンで削除されたいくつかのバグに関連しています。

私はAngular6を使用しています。MessageServiceを使用して認証コンポーネントとアプリコンポーネントの間で通信し、ユーザーがログインしている(またはログインしていない)場合に異なるナビゲーションバーのコンテンツを表示しています

このエラーは別として、問題なく動作します。
EventEmitterなどの他のソリューションを試しましたが、機能しません。 または、同じエラーが発生します。

@dotNetAthlete ...

@dotNetAthleteメインコンテナコンポーネントに動作サブジェクト(asObservable)としてページタイトルを含む状態サービスがあり、状態サービスに適切なページタイトルを設定する子コンポーネントがロードされているのと同じ基本メカニズムを使用しています親コンテナが監視および表示するため。

コンテナが値を初期化し、子コンポーネントがすぐに値を更新すると、常に「式が変更されました」というエラーが発生したため、親/コンテナでこれを実行します。

    this.stateSvc.currentPageTitle$
      .subscribe(
        (async (pageTitle) => {
          this.pageTitle = await pageTitle;
        }))

州のサービスがこれを持っているところ:

  // Store
  private currentPageTitleStore = new BehaviorSubject<string>("");

  // Getter (as Observable)
  public currentPageTitle$ = this.currentPageTitleStore.asObservable();

  // Setter
  public setCurrentPageTitle(pageTitle: string) {
    this.currentPageTitleStore.next(pageTitle);
  }

これにより、問題が解消されます。 このソリューションは、このスレッドの他のユーザーからの有益なフィードバックに基づいています。

@ alignsoft-美しいソリューション

私は今Angular7のページリロードでこれを見ています...
親に子コンポーネントの値を設定していますngAfterViewInit
子コンポーネントでこれを行う必要がありました

  public activate() {
    setTimeout(() => this.active = true);
  }

この問題のスレッドhttps://github.com/angular/angular/issues/10762で解決策を見つけました
AfterContentInitライフサイクルフックを使用した後、エラーはなくなります。

@ alignsoft 、Subjectの代わりにBehavioralSubjectを使用しているため、変更を追加するだけです。

私はそれを次のように洗練しました:

\\ Component
export class AppComponent implements OnInit {
  isLoading: Boolean;

  constructor(private loaderService: LoaderService) { }

  ngOnInit(){
    this.loaderService.isLoading.subscribe(async data => {
      this.isLoading = await data;
    });
  }
}
\\ Service
@Injectable({
  providedIn: 'root'
})
export class LoaderService {
  // A BehaviorSubject is an Observable with a default value
  public isLoading: BehaviorSubject<Boolean> = new BehaviorSubject(false);
  constructor() {}
}
\\ Any other component or in my case, an interceptor that has the LoaderService injected
this.loaderService.isLoading.next(true);

しかし、私は@daddyschmackに同意します。上記の修正( cdr.detectionChanges()またはasync awaitまたはaftercontentchecked )のいずれかに関する情報を指すリソースを取得したいのはなぜですか。 [hidden]属性は機能し、 *ngIfでは機能しません

@acidghostそのリンクをありがとう、それは私の問題を解決しました。 私は実際にアプリケーションのある領域でそれを修正しようとするのをあきらめていましたが、それが新しい領域で再び起こり始めたとき...

この問題は、非アクティブのために自動的にロックされています。
同様の問題または関連する問題が発生した場合は、新しい問題を提出してください。

自動会話ロックポリシーの詳細をご覧ください。

_このアクションはボットによって自動的に実行されました。_

このページは役に立ちましたか?
0 / 5 - 0 評価