Search code examples
rxjsobservablengrxangular2-observables

How to retrieve the latest state from an RxJS observable?


In this ngrx example, searchQuery$ is an RxJS Observable. I understand that this Observable emits changes when the search query string changes on the UI.

I have introduced another Observable called paging$ to this control to do the paging. This Observable emits the page number. When the page changes, I have to make another server call to retrieve the remaining books.

When I make the call to the server when the user clicks the page, I have the page number because it comes directly from the UI, but not the search string which is already emitted by the searchQuery$ Observable.

How can I combine searchQuery$ and paging$ to retrieve both the search string and the page number using RxJS?

UPDATE:

Events:

  1. User types an in the search box.
  2. The first 10 books are displayed.
  3. The user now clicks the NEXT paging button.
  4. The next 10 books should be displayed (after a http request with the last search string an).

The code from ngrx example:

import 'rxjs/add/operator/let';
import 'rxjs/add/operator/take';
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs/Observable';

import * as fromRoot from '../reducers';
import * as book from '../actions/book';
import { Book } from '../models/book';

@Component({
  selector: 'bc-find-book-page',
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: '
    <bc-book-search [query]="searchQuery$ | async" [searching]="loading$ | async" (search)="search($event)"></bc-book-search>
    <bc-book-preview-list [books]="books$ | async"></bc-book-preview-list>
  '
})
export class FindBookPageComponent {
  searchQuery$: Observable<string>;
  books$: Observable<Book[]>;
  loading$: Observable<boolean>;

  constructor(private store: Store<fromRoot.State>) {
    this.searchQuery$ = store.select(fromRoot.getSearchQuery).take(1);
    this.books$ = store.select(fromRoot.getSearchResults);
    this.loading$ = store.select(fromRoot.getSearchLoading);
  }

  search(query: string) {
    this.store.dispatch(new book.SearchAction(query));
  }
}

Solution

  • You could use a combineLatest:

    updateBooks$ = Observable.combineLatest(
        this.searchQuery$,
        this.paging$
    ).do(([searchQuery, paging]: [string, number]) => {
        ...makeServerRequest or dispatch an action here
    });
    
    updateBooks$
        // when subscribing in a component directly, don't forget to handle component-destructions: http://stackoverflow.com/questions/42490265/rxjs-takeuntil-angular2-components-ngondestroy
        .takeUntil(destroyed$)
        .subscribe();
    

    *in case your code is identical to the ngrx-example, you will have to remove the take(1) from the store-select on the searchQuery$