2019年6月30日日曜日

AngularのdetectChanges()で「ViewDestroyedError: Attempt to use a destroyed」が発生した時の対応方法。


オフィス狛 技術部のKoma(Twitterアカウントの中の人&CEO)です。

私の担当がいつの間にかAngular専任になっていますが・・・・今回もやっぱりAngularネタです。

Angularで、NgRxなどでAPIから値を取得した場合、そのままView(template・html)側にデータを流し込んであげれば、取得したデータは問題なく表示されます。
ところが、component(TypeScript)側で取得したデータをstore.selectで取得すると、View(template・html)側に表示データが反映されない場合があります。

うーん、言葉だけで説明するのが難しい。
この辺はいつかNgRxの使い方とかで詳しく説明したいですが、前者の場合、ストリーム(川の流れ)は繋ぎっぱなしですが、
後者は、一度ストリーム(川の流れ)から、データ取っているので、流れが止まっている、という事ですね。

強制的に画面に反映させる為には、「changeDetectorRef の detectChanges() 」メソッドを使います。

では、ここで使い方の例を一つ。
import { Component, ChangeDetectorRef, OnInit } from '@angular/core';
// (中略)
export class HogeKomaComponent implements OnInit {
// (中略)
  constructor(
    public store: Store,
    private changeDetectorRef: ChangeDetectorRef
  ) {}
 
  ngOnInit() {
    this.hogeKomaSubscription.push(
      this.store
        .select(fromHoge.getKomaList)
        .pipe(skip(1))
        .subscribe(result => {
            //
            // ここで取得したデータを編集し、Template側の変数へ再設定(これだけだと、画面は更新されない)
            //
 
            // 画面の表示を更新する(これを実施する事で画面が更新される)
            this.changeDetectorRef.detectChanges();
    }));
 
    // データ取得
    this.store.dispatch(new KomaListActions.GetKomaList());
  }

で、ここからが本題なのですが、「detectChanges()」を使っていると、以下のようなエラーが発生する事があります。
    common.96cfb6ba445916612966.js:1 ERROR Error: ViewDestroyedError: Attempt to use a destroyed view: detectChanges
    at pm (main.35927d7a5d9b4fa62e08.js:1)
    at Object.m_ [as updateDirectives] (main.35927d7a5d9b4fa62e08.js:1)
    at jv (main.35927d7a5d9b4fa62e08.js:1)
    at D_ (main.35927d7a5d9b4fa62e08.js:1)
    at Object.i_ [as checkAndUpdateView] (main.35927d7a5d9b4fa62e08.js:1)
    at n.detectChanges (main.35927d7a5d9b4fa62e08.js:1)
    at e._next (10.ab643854ddb1ed074304.js:1)
    at e.__tryOrUnsub (main.35927d7a5d9b4fa62e08.js:1)
    at e.next (main.35927d7a5d9b4fa62e08.js:1)
    at e._next (main.35927d7a5d9b4fa62e08.js:1)

エラーメッセージは、
ViewDestroyedError: Attempt to use a destroyed view: detectChanges
という事ですが、要は、「画面表示を行おうとしたけど、もう対象のViewが存在しない」という事ですね。

こういう場合は、
  if (!this.changeDetectorRef['destroyed']) {
    this.changeDetectorRef.detectChanges();
  }

上記のように、既にViewが破棄されていないか確認する事で回避出来ます。

今回も内容の割には長くなってしまいました・・・・
(エラーメッセージと対応内容書けば、ある意味終了なんですけどね。)

こんな内容でも、誰かの役に立つ事を願って・・・・

では、より良いAngularライフを!


0 件のコメント:

コメントを投稿