1

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));
  }
}
Mark van Straten
  • 9,287
  • 3
  • 38
  • 57
wonderful world
  • 10,969
  • 20
  • 97
  • 194
  • 1
    The downvote is not from me, but a hint regarding your question: Please include code directly on stackoverflow, because the external code might change over time or might be taken offline or the link might break. – olsn Mar 14 '17 at 18:32

1 Answers1

1

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$

olsn
  • 16,644
  • 6
  • 59
  • 65
  • Does this mean, any observable will keep it's latest state and can be retrieved as above? – wonderful world Mar 14 '17 at 17:49
  • 1
    Not any observable, but if you have a `select`-statement on your store it will be the case - and additionally: `combineLatest` will hold the latest state of the given observables and emit an array of all the latest states whenever one of the given observables emits data. – olsn Mar 14 '17 at 17:58
  • As I said in the original question, ```this.searchQuery$``` is not changing in the UI. Only the paging events changing. So, doesn't the value ```this.searchQuery$``` observable emit is empty rather than what the user entered on the UI? What I dont understand is there is no ```subject``` that is emitting search string for this ```observable``` to combine with. – wonderful world Mar 14 '17 at 18:14
  • 1
    I don't think I understand your issue - just in case you have built your example on top of the example-app, you would of course have to remove the `take(1)` after the store-select, because that would only trigger the update from the store once. – olsn Mar 14 '17 at 18:19
  • 1
    Whenever the user types something in the search-input, the store is updated with the searchQuery - if nothing was ever entered, then it is just an empty string. – olsn Mar 14 '17 at 18:21
  • I updated the question with the action sequence. I hope that can clarify my doubt. – wonderful world Mar 14 '17 at 18:28
  • 1
    Yes, did you test my solution yet? It should work for this case – olsn Mar 14 '17 at 18:29
  • Yes. That worked. I declared the ```updateBooks$``` in the component constructor. – wonderful world Mar 17 '17 at 22:34