web-dev-qa-db-ja.com

配列変異を伴うAngular2 ngFor OnPush変更検出

データテーブルコンポーネント( angular2-data-table )プロジェクトがあり、プロジェクトを最適化されたレンダリング速度のために、Angularの従来の変更検出からOnPushに変更しました。

新しい変更検出戦略が実装されると、オブジェクトのプロパティの更新など、データオブジェクトが変更されたときにテーブルが更新されないことを参照するバグが報告されました参照: https://github.com/swimlane/angular2-data- table/issues/255 。強力なユースケースは、インラインティッカーや株式ティッカーなどの大規模なデータコレクション内の単一のプロパティに対する外部データの変更など、この種のニーズに対応できます。

この問題を解決するために、trackByPropと呼ばれるカスタムtrackByプロパティチェッカーを追加しました。参照: コミット 。残念ながら、この解決策は問題を解決しませんでした。

デモページ ライブリロードでは、上記のコミットで参照されているデモが実行されていますが、クリックして変更検出をトリガーするまでテーブルは更新されません。

コンポーネントの構造は次のようなものです。

Table > Body > Row Group > Row > Cell

これらのコンポーネントはすべて、implement OnPushです。行セッターでゲッター/セッターを使用して、示されている here のようなページの再計算をトリガーしています。

このパターンを実装している人はOnPushchangeの検出を継続したいと考えていますが、複数のコンシューマーを使用するオープンソースプロジェクトとして、画面に表示される行の値について何らかのカスタムチェック機能を主張できます。

つまり、trackByは行セル値の変更検出をトリガーしません。これを達成する最良の方法は何ですか?

14
amcdnl

Angular2の変更検出では、配列またはオブジェクトの内容はチェックされません。

ハッキングの回避策は、突然変異後に配列のコピーを作成することです

this.myArray.Push(newItem);
this.myArray = this.myArray.slice();

こちらです this.myArrayは異なる配列インスタンスを参照し、Angularは変更を認識します。

別のアプローチは、IterableDiffer(配列用)またはKeyValueDiffer(オブジェクト用)を使用することです

// inject a differ implementation 
constructor(differs: KeyValueDiffers) {
  // store the initial value to compare with
  this.differ = differs.find({}).create(null);
}

@Input() data: any;

ngDoCheck() {
  var changes = this.differ.diff(this.data); // check for changes
  if (changes && this.initialized) {
    // do something if changes were found
  }
}

https://github.com/angular/angular/blob/14ee75924b6ae770115f7f260d720efa8bfb576a/modules/%40angular/common/src/directives/ng_class.ts#L122

24

markForCheckChangeDetectorRefメソッドを使用する場合があります


同様の問題があります。多くのデータを含むコンポーネントがあり、すべての変更検出サイクルでそれらをすべて再チェックするオプションはありません。しかし、[〜#〜] url [〜#〜]からいくつかのプロパティを監視し、それに応じてビューの内容をonPushビューは更新されません(自動的に)。

したがって、コンストラクターで、[〜#〜] di [〜#〜]を使用してchangeDetectorRefのインスタンスを取得します:constructor(private changeDetectorRef: ChangeDetectorRef)

そして、changeDetectionをトリガーする必要がある場所:this.changeDetectorRef.markForCheck();

3
maxime1992

私もアプリのパフォーマンスを最適化するためにchangeDetection.OnPush戦略を使用する必要があるという同様の問題に直面しました。それで、親コンポーネントと子コンポーネントのコンストラクタの両方にchangeDetectorRefのインスタンスを注入しました

    export class Parentcomponent{
       prop1;

       constructor(private _cd : ChangeDetectorRef){
          }
       makeXHRCall(){
        prop1 = ....something new value with new reference;
        this._cd.markForCheck(); // To force angular to trigger its change detection now
       }
    }

同様に、子コンポーネントにchangeDetectorRefのインスタンスを注入しました

    export class ChildComponent{
     @Input myData: myData[];
     constructor(private _cd : ChangeDetectorRef){
          }
       changeInputVal(){
        this.myData = ....something new value with new reference;
        this._cd.markForCheck(); // To force angular to trigger its change detection now
       }
    }

角度変化の検出は、すべての非同期機能でトリガーされます:-

  • クリック、送信、マウスオーバーなどのDOMイベント。
  • xHR呼び出し
  • setTimeout()などのタイマー.

したがって、マウスをドラッグしているときでもangularがchangeDetectionをトリガーしていたため、この種のアプリは遅くなります。複数のコンポーネントにまたがる複雑なアプリの場合、これは= angularには、この種の親から子への変更検出戦略があります。これを回避するには、OnPush戦略を使用し、参照の変更がある場合に角度の変更検出を強制的にトリガーすることをお勧めします。

第二に、OnPush戦略では、オブジェクトプロパティ値だけでなく、オブジェクト参照に変更がある場合にのみ変更をトリガーすることに非常に注意する必要があります。つまり、in Angular change 」。

例えば:-

    obj = { a:'value1', b:'value2'}'
    obj.a = value3;

'obj'の 'a'のプロパティ値は変更されている可能性がありますが、objはまだ同じ参照を指しているため、Angular変更検出はここでトリガーされません(強制しない限り);作成するには新しい参照、オブジェクトを別のオブジェクトに複製し、それに応じてプロパティを割り当てる必要があります。

理解を深めるために、不変データ構造についてお読みください。変更検出 ここ

2
Gaurav Pandvia

遅い回答ですが、別の回避策は、突然変異後にスプレッド演算子を使用することです。_myArr = [...myArr]_または_myObj = {...myObj}_

これは、変更中に行うこともできます:myArr = myMutatingArr([...myArr])パラメーターは配列の新しい参照として取得され、変数が新しい参照を取得するため、Angular checkと呼ばれます。

前述のように、参照を変更すると、チェックが行われ、スプレッド演算子を使用して正確にそれを行うことができます。

ただし、データ構造内のデータのネスト構造では、ネストレベルまで参照を変更する必要があることに注意してください。次のように、スプレッド内のスプレッドを返す反復を実行する必要があります。

_myObj = {...myObj, propToChange: { ...myObj.propToChange, nestedPropArr: [ ...myObj.propToChange.nestedPropArr ] } }_

オブジェクトなどの反復が必要な場合、これは複雑になる可能性があります。これが誰かを助けることを願っています!