概要
AngularのPipeの中にはAync PipeというPromiseやObservableな非同期オブジェクトをそのままtemplateで表示できるPipeがあります。 今回はその使い方を紹介します。
環境
- angular 2.4.8
- rxjs 5.2.0
async pipeのメリット
主なメリットは以下の2つです。
フロントではメモリ管理が大切なので、特に後者は嬉しいメリットですね。
基本的な使い方
@Component({ selector: 'async-pipe', template: '<div>Time: {{ date | async }}</div>' }) export class AsyncPipeComponent { public date: Observable<Date> = Observable.create( observer => { const now = Date.now(); observer.next(now); observer.complete(); }); }
https://embed.plnkr.co/AJzEjrWzfxq7i2PF7sqH/
結果
Time: 1489196337153
Promise
やObservable
なオブジェクトをtemplate
側で| async
と一緒に置くことで、その時のObservable<T>
のT
型のオブジェクトを表示することができます。
特定のフィールドを表示したい場合
複数のメンバを持ったUserオブジェクトを渡したい、ということもあると思います。
class User { name: string; age: number; } @Component({ selector: 'async-pipe', template: '<div>User: {{ (user | async)?.name }}</div>' }) export class AsyncPipeComponent { public user: Observable<User> = Observable.create( observer => { const user: User = { name: 'Tom', age: 20 }; observer.next(user); observer.complete(); }); }
https://embed.plnkr.co/oeMEsuZ1LN7u5egnw9cC/
結果
User: Tom
このように()
でくくると、オブジェクトとして扱うことができます。
複数のasync pipeがある時
templateに複数のasync pipeがある場合、Cold Observableだとその数の分callされてしまいます。
NG
@Component({ selector: 'async-pipe', template: ` <div>Time1: {{ date | async }}</div> <div>Time2: {{ date | async }}</div> ` }) export class AsyncPipeComponent { public date: Observable<Date> = Observable.create( observer => { console.log('called'); const now = Date.now(); observer.next(now); observer.complete(); }); }
https://embed.plnkr.co/o3zx1xLOqbXAx7e4yJFM/
結果
// called // called Time1: 1489196337153 Time2: 1489196337154
同じオブジェクトなのにわざわざ2回呼ぶことになってしまい、無駄な処理となります。
これはHot Observableに変換することで、1度だけにすることができます。
OK
@Component({ selector: 'async-pipe', template: ` <div>Time1: {{ date | async }}</div> <div>Time2: {{ date | async }}</div> ` }) export class AsyncPipeComponent { public date: Observable<Date> = Observable.create( observer => { console.log('called'); const now = Date.now(); observer.next(now); observer.complete(); }) .share(); }
https://embed.plnkr.co/wEy7SdCUoakAdk8WDRWv/
※plunkerではきちんと表示されませんが、通常のangular2では表示されます
結果
// called Time1: 1489196337153 Time2: 1489196337153
@Inputにasync pipeをセットする場合
子コンポーネントの@Inputにasync pipeを設定する場合、@Inputにはまずnullが渡されてしまうので、ちゃんとrenderされないです。
NG
Parent Component
@Component({ selector: 'async-pipe', template: '<pipe-child [text]="(date | async)"></pipe-child>' }) export class AsyncPipeComponent { public date: Observable<Date> = Observable.create( observer => { const now = Date.now(); observer.next(now); observer.complete(); }); }
Child Component
@Component({ selector: 'pipe-child', template: '<span>Time: {{text}}</span>' }) export class AsyncPipeComponent { @Input text: Date; }
結果
Time: null
OK
Parent Component
Parent Componentはそのままで大丈夫です。
Child Component
@Component({ selector: 'pipe-child', template: '<span>Time: {{internalText}}</span>' }) export class AsyncPipeComponent implements OnChanges { @Input text: Date; public internalText: Date; ngOnChanges(changes: any) { if (changes.text) { this.internalText = changes.text.currentValue; } } }
このようにngOnChanges
を使って、後から変更するように修正します。
結果
TIme: 1489196337153
ちゃんと表示されるようになりました。
まとめ
Async Pipeの使い方を紹介しました。
そのViewで一度しか使わないようなObservableな値は、async pipeを使ってtemplate側に書いた方がコードがシンプルになります。
一方で「ボタンを押したら状態が更新される」ようなケースでは、component側でsubscribeした方が良いです。