web-dev-qa-db-ja.com

配列プッシュでのAngular2更新ビュー

SetInterval非同期操作から呼び出されたarray.Push関数で、angular2ビューを更新することはできません。

コードはこれから setIntervalの角形プランカーの例

私がやろうとしていることは次のとおりです:

import {View, Component, bootstrap, Directive, ChangeDetectionStrategy, ChangeDetectorRef} from 'angular2/angular2'

@Component({selector: 'cmp', changeDetection: ChangeDetectionStrategy.OnPush})
@View({template: `Number of ticks: {{numberOfTicks}}`})
class Cmp {
  numberOfTicks = [];
  
  constructor(private ref: ChangeDetectorRef) {
    setInterval(() => {
      this.numberOfTicks.Push(3);
      this.ref.markForCheck();
    }, 1000);
  }
}

@Component({
  selector: 'app',
  changeDetection: ChangeDetectionStrategy.OnPush
})
@View({
  template: `
    <cmp><cmp>
  `,
  directives: [Cmp]
})
class App {
}

bootstrap(App);
<!DOCTYPE html>
<html>

<head>
  <title>angular2 playground</title>
  <script src="https://code.angularjs.org/tools/traceur-runtime.js"></script>
  <script src="https://code.angularjs.org/tools/system.js"></script>
  <script src="https://code.angularjs.org/tools/TypeScript.js"></script>
  <script data-require="jasmine" data-semver="2.2.1" src="http://cdnjs.cloudflare.com/ajax/libs/jasmine/2.2.1/jasmine.js"></script>
  <script data-require="jasmine" data-semver="2.2.1" src="http://cdnjs.cloudflare.com/ajax/libs/jasmine/2.2.1/jasmine-html.js"></script>
  <script data-require="jasmine@*" data-semver="2.2.1" src="http://cdnjs.cloudflare.com/ajax/libs/jasmine/2.2.1/boot.js"></script>
  
  <script src="config.js"></script>
  <script src="https://code.angularjs.org/2.0.0-alpha.37/angular2.min.js"></script>
  <script>
    System.import('app')
      .catch(console.error.bind(console));
  </script>

</head>

<body>
  <app></app>
</body>

</html>

上記のコードは、「numberOfTicks」が単なる数字である場合(plunkerの元の例が示すように)適切に機能しますが、配列に変更してデータをプッシュすると、更新されません。

なぜだか理解できないようです。

次の動作は、setInterval/setTimeoutを使用しているときに、angular2のグラフを新しいデータポイントで更新しようとするときの問題に似ています。

助けてくれてありがとう。

13
Omer Kushmaro

要素を追加した後、配列の参照全体を更新する必要があります。

  constructor(private ref: ChangeDetectorRef) {
    setInterval(() => {
      this.numberOfTicks.Push(3);
      this.numberOfTicks = this.numberOfTicks.slice();
      this.ref.markForCheck();
    }, 1000);
  }
}

編集

DoCheckインターフェースは、独自の変更検出アルゴリズムをプラグインできるため、興味を引くこともあります。

詳細については、次のリンクを参照してください。

サンプルを次に示します。

@Component({
  selector: 'custom-check',
  template: `
    <ul>
      <li *ngFor="#line of logs">{{line}}</li>
    </ul>`
})
class CustomCheckComponent implements DoCheck {
  @Input() list: any[];
  differ: any;
  logs = [];

  constructor(differs: IterableDiffers) {
    this.differ = differs.find([]).create(null);
  }

  ngDoCheck() {
    var changes = this.differ.diff(this.list);
    if (changes) {
      changes.forEachAddedItem(r => this.logs.Push('added ' + r.item));
      changes.forEachRemovedItem(r => this.logs.Push('removed ' + r.item))
    }
  }
}
19

上記のコードは、「numberOfTicks」が単なる数字の場合は適切に機能しますが、配列に変更してデータをプッシュすると、更新されません。それがなぜなのか理解できないようです。

これは、数値がJavaScriptプリミティブ型であり、配列がJavaScript参照型であるためです。 Angular change detectionが実行されると、_===_を使用して、テンプレートバインディングが変更されたかどうかを判断します。

_{{numberOfTicks}}_がプリミティブ型の場合、_===_は現在の値と以前の値を比較します。したがって、値_0_と_1_は異なります。

_{{numberOfTicks}}_が参照型の場合、_===_は現在の値と以前の(参照)値を比較します。したがって、配列参照が変更されなかった場合、Angularは変更を検出しません。あなたの場合、Push()を使用すると、配列参照は変更されません。

代わりに次をテンプレートに入れた場合

_<div *ngFor="#tick of numberOfTicks">{{tick}}</div>
_

then Angularは、配列そのものではなく、各配列要素へのバインディングがあるため、ビューを更新します。新しいアイテムがPush() edの場合、または既存のアイテムの場合が削除または変更された場合、これらの変更はすべて検出されます。

したがって、チャートプランカーでは、fromおよびto配列の内容が変更されると、以下が更新されます。

_<span *ngFor="#item of from">{{item}}</span>
<span *ngFor="#item of to">{{item}}</span>
_

まあ、各アイテムがプリミティブ型の場合は更新されます。

9
Mark Rajcok