非同期パイプを使用したAngularでのサブスクリプションの管理
Angular 2+に組み込まれている非同期パイプは、監視可能なサブスクリプションを簡単に管理するための優れたツールを提供します。 これにより、ほとんどの場合、コンポーネントクラスのオブザーバブルを手動でサブスクライブする必要がなくなることを学ぶことができます。
毎秒現在の時刻を表示するオブザーバブルが必要だとします。 非同期パイプを使用せずに、次のようなことを行う可能性があります。
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription';
import 'rxjs/add/observable/interval';
import 'rxjs/add/operator/map';
@Component({
selector: 'app-root',
template: `Time: {{ time | date:'mediumTime' }}`
})
export class AppComponent implements OnInit, OnDestroy {
time: Date;
timeSub: Subscription;
ngOnInit() {
this.timeSub = Observable
.interval(1000)
.map(val => new Date())
.subscribe(val => this.time = val);
}
ngOnDestroy() {
this.timeSub.unsubscribe();
}
}
OnInit フックで、毎秒値を発行し、その値を新しい日付にマップするオブザーバブルを作成しました。 次に、そのオブザーバブルをサブスクライブし、timeクラス変数を出力された値に設定しました。 また、コンポーネントが破壊されたときにオブザーバブルのサブスクライブを解除して、後でクリーンアップするようにしました。
テンプレートでは、組み込みの date パイプを使用して、日付を目的の分と秒の形式に変換しました。
ただし、これはかなりの定型コードであり、サブスクライブを解除するのを忘れると、メモリリークが発生するリスクがあります。 大幅に簡略化できます。代わりに、非同期パイプを使用して実装された同じ機能を次に示します。
import { Component } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/interval';
import 'rxjs/add/operator/map';
@Component({
selector: 'app-root',
template: `Time: {{ time$ | async | date:'mediumTime' }}`
})
export class AppComponent {
time$ = Observable
.interval(1000)
.map(val => new Date());
}
非同期パイプは、データのサブスクライブとアンラップ、およびコンポーネントが破棄されたときのサブスクライブ解除を処理します。
非同期パイプを使用して、データをアンラップし、子コンポーネントの入力に渡すこともできます。
<app-child [time]="time$ | async"></app-child>
そして、子供は今、実際に行うことではなく、データを表示する必要があります。
ngForを使用した非同期パイプ
オブザーバブルとして利用できるもう少し複雑なデータ構造があり、その値を取得する前に1秒の人為的な遅延を設定したとします(ネットワーク要求を模倣します)。
import { Component } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/delay';
@Component({ ... })
export class AppComponent {
cities$ = Observable
.of([
{name: 'Los Angeles', population: '3.9 million', elevation: '233′'},
{name: 'New York', population: '8,4 million', elevation: '33′'},
{name: 'Chicago', population: '2.7 million', elevation: '594′'},
])
.delay(1000);
}
テンプレートでは、次のような ngFor ディレクティブを使用してデータが到着したときに、データをアンラップしてサブスクライブできます。
<ul>
<li *ngFor="let city of cities$ | async">
Name: {{ city.name }},
Population: {{ city.population }},
Elevation: {{ city.elevation }}</li>
</ul>
ngIfを使用した非同期パイプ
ngIf構造ディレクティブを使用した例を次に示します。 オブザーバブルは次のようになります。
word$ = Observable.of('Abibliophobia');
そして、私たちのテンプレートは次のようになります。
<span *ngIf="(word$ | async)?.length > 9; else shortWord">
Long word: {{ word$.value }}
</span>
<ng-template #shortWord>
Short word: {{ word$.value }}
</ng-template>
そして、次のようになります。
Long word: Abibliophobia
word $ を非同期でパイプ処理する方法に注目してください。ただし、ラップされていない値の長さを確認できるように、括弧で囲んでください。 また、エルビス演算子(?)を使用して、word$の値がまだ使用できない場合のエラーを回避します。