14

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

新しい変更検出戦略が実装されると、オブジェクトのプロパティの更新など、データ オブジェクトが変更されたときにテーブルが更新されないことを参照するバグが報告されました。 255 . インライン編集や、株価情報などの大規模なデータ コレクション内の 1 つのプロパティに対する外部データの変更など、この種のニーズに対して強力なユース ケースを作成できます。

この問題を解決するために、 というカスタム trackBy プロパティ チェッカーを追加しましたtrackByProp。参考:コミット。残念ながら、このソリューションでは問題は解決しませんでした。

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

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

Table > Body > Row Group > Row > Cell

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

このパターンを実装する人のために変更検出を続けたいと思いますがOnPush、複数のコンシューマーを含むオープンソース プロジェクトとして、画面に表示される行の値に対する何らかのカスタム チェック機能について議論することができます。

とは言っても、trackBy行セル値の変更検出をトリガーしていません。これを達成するための最良の方法は何ですか?

4

4 に答える 4

25

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も参照してください。

于 2016-11-27T14:25:02.620 に答える
3

markForCheckのメソッドを使用することもできますChangeDetectorRef


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

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

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

于 2016-11-27T14:34:57.220 に答える
2

私も同様の問題に直面し、アプリのパフォーマンスを最適化するには、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 戦略では、オブジェクト プロパティ値だけでなく、オブジェクト参照に変更がある場合にのみ変更をトリガーするように十分注意する必要があります。つまり、Angular の変更では「新しい参照」を意味します。

例:-

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

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

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

于 2017-04-23T09:40:22.313 に答える