Angular 4のコンポーネントとサービスのObservableオブジェクトの典型的なユースケースを紹介します。
ルーターパラメーターのサブスクライブと別のObservableへのマッピング
タスク: example.com/#/users/42
userId
ページを開くと、 userId
でユーザーデータを取得します。
解決策: UserDetailsComponent
コンポーネントを初期化するときに、ルーターのパラメーターをサブスクライブします。 つまり、 userId
が変更されると、サブスクリプションがuserId
ます。 受信したuserId
を使用して、 userService
サービスからのユーザーデータでObservable
を取得します。
// UserDetailsComponent ngOnInit() { this.route.params .pluck('userId') // userId .switchMap(userId => this.userService.getData(userId)) .subscribe(user => this.user = user); }
ルーターパラメーターとクエリ文字列のサブスクライブ
タスク: example.com/#/users/42?regionId=13
load(userId, regionId)
ページを開くとき、 load(userId, regionId)
機能load(userId, regionId)
を実行する必要があります。 ルーターからuserId
を取得し、リクエストパラメーターからregionId
を取得します。
解決策:イベントのソースは2つあるため、 Observable.combineLatest関数を使用します。この関数は、各ソースがイベントを生成するときに起動します。
ngOnInit() { Observable.combineLatest(this.route.params, this.route.queryParams) .subscribe(([params, queryParams]) => { // const userId = params['userId']; const regionId = queryParams['regionId']; this.load(userId, regionId); }); }
ルーターに作成されたサブスクリプションは、オブジェクトが破棄されると削除されることに注意してください。角度はこれに従っているため、ルーターのパラメーターのサブスクリプションを解除する必要はありません。
ルーターは、提供するオブザーバブルを管理し、サブスクリプションをローカライズします。 コンポーネントが破壊されると、サブスクリプションはクリーンアップされ、メモリリークから保護されるため、ルートパラメーターObservableからサブスクリプションを解除する必要はありません。 マーク・レイコック
サブスクリプションの終了後にアニメーションの読み込みを停止します
タスク:データの保存を開始した後にダウンロードアイコンを表示し、データの保存時またはエラーが発生したときに非表示にします。
解決策: loading
変数はブートローダーの表示を担当し、ボタンをクリックした後、それをtrue
に設定しtrue
。 false
に設定するには、 Observable.finally
関数false
使用します。これらの関数は、サブスクリプションの完了後またはエラーが発生した場合に実行されます。
save() { this.loading = true; this.userService.save(params) .finally(() => this.loading = false) .subscribe(user => { // }, error => { // }); }
独自のイベントソースを作成する
タスク: configService
でlang$
変数を作成します。他のコンポーネントは、言語が変更されたときにサブスクライブして応答します。
解決策: BehaviorSubject
クラスを使用してlang$
変数を作成します。
BehaviorSubject
とSubject
違い :
-
BehaviorSubject
は初期値で初期化する必要があります。 - サブスクリプションは、
Subject
aの最後の値を返します。 -
getValue()
関数を使用して、最後の値を直接取得できます。
lang$
変数を作成し、すぐに初期化します。 また、言語を設定するsetLang
関数を追加します。
// configService lang$: BehaviorSubject<Language> = new BehaviorSubject<Language>(DEFAULT_LANG); setLang(lang: Language) { this.lang$.next(this.currentLang); // }
コンポーネントの言語変更にサブスクライブします。 lang$
変数は、ホットなObservableオブジェクトです。つまり、オブジェクトが破棄されると、サブスクリプションはサブスクリプションを解除する必要があります。
private subscriptions: Subscription[] = []; ngOnInit() { const langSub = this.configService.lang$ .subscribe(() => { // ... }); this.subscriptions.push(langSub); } ngOnDestroy() { this.subscriptions .forEach(s => s.unsubscribe()); }
takeUntilを使用して登録を解除する
特にコンポーネントに3つ以上のサブスクリプションがある場合、 よりエレガントなオプションでサブスクリプションを解除できます。
private ngUnsubscribe: Subject<void> = new Subject<void>(); ngOnInit() { this.configService.lang$ .takeUntil(this.ngUnsubscribe) // .subscribe(() => { // ... }); } ngOnDestroy() { this.ngUnsubscribe.next(); this.ngUnsubscribe.complete(); }
つまり、ホットサブスクリプションでメモリが失われないように、 ngUnsubscribe
の値が変更されるまでコンポーネントはngUnsubscribe
ます。 そして、ngOnDestroyがngOnDestroy
と変更さngOnDestroy
ます。 このオプションの利点は、各サブスクリプションに1行だけ追加すれば十分であり、サブスクリプションが時間どおりに機能することです。
オートコンプリートまたは検索にObservableを使用する
タスク:フォームにデータを入力するときにページの提案を表示する
解決策:フォームデータを変更するためにサブスクライブし、変更する入力データのみを取得し、イベントが多すぎないように少し遅延させて、Wikipediaにリクエストを送信します。 結果はコンソールに表示されます。 興味深い点は、新しいデータswitchMap
場合にswitchMap
が以前の要求をキャンセルswitchMap
です。 たとえば、最後から2番目のリクエストが2秒間実行され、最後の0.2秒間実行された場合、最後のリクエストの結果がコンソールに表示されます。
ngOnInit() { this.form.valueChanges .takeUntil(this.ngUnsubscribe) // .map(form => form['search-input']) // .distinctUntilChanged() // .debounceTime(300) // .switchMap(this.wikipediaSearch) // Observable .subscribe(data => console.log(data)); } wikipediaSearch = (text: string) => { return Observable .ajax('https://api.github.com/search/repositories?q=' + text) .map(e => e.response); }
リクエストのキャッシュ
タスク: Observableリクエストをキャッシュする必要があります。
解決策: publishReplay
とrefCount
束を使用します。 最初の関数は1つの関数値を2秒間キャッシュし、2番目の関数は作成されたサブスクリプションをカウントします。 つまり、すべてのサブスクリプションが完了すると、Observableは終了します。 ここでもっと読むことができます。
// tagService private tagsCache$ = this.getTags() .publishReplay(1, 2000) // 2 .refCount() // .take(1); // 1 getCachedTags() { return tagsCache$; }
シリアルコンバイン
タスク:サーバー上の重大な状況! バックエンドチームは、製品の正しい更新のために厳密に順番に実行する必要があると述べました。
- 製品データ(タイトルと説明)の更新。
- 製品タグのリストを更新します。
- 製品カテゴリのリストを更新します。
解決策: productService
から派生した3つのObservableがproductService
ます。 concatMap
を使用しconcatMap
。
const updateProduct$ = this.productService.update(product); const updateTags$ = this.productService.updateTags(productId, tagList); const updateCategories$ = this.productService.updateCategories(productId, categoryList); Observable .from([updateProduct$, updateTags$, updateCategories$]) .concatMap(a => a) // .toArray() // .subscribe(res => console.log(res)); // res
スタッフのなぞなぞ
少し練習したい場合は、前の問題を解決しますが、製品を作成します。 つまり、最初に製品を作成し、次にタグを更新してから、カテゴリのみを更新します。
便利なリンク
- ボールのスペルバウンドルック: rxviz.com
- マウスでボールをドラッグ: rxmarbles.com