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のグラフを新しいデータポイントで更新しようとするときの問題に似ています。
助けてくれてありがとう。
要素を追加した後、配列の参照全体を更新する必要があります。
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))
}
}
}
上記のコードは、「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>
_
まあ、各アイテムがプリミティブ型の場合は更新されます。