jobou
jobou

Reputation: 1863

Rxjs Observable : subscribe to different values with one API call

I have a CRUD API where the get on the entity returns both the list and the total number of items for the current filter configuration.

For example : GET /posts

{
    "items": [
        {
             "id": 1,
             "title": "post title 1"
        }
        ...
    ],
    "total": 9
}

How can I create an observable where the HTTP query is executed only one time but where I can subscribe to either the total value or the items list ?

I am in an Angular2 context with typescript.

I first tried this and it worked :

this.http.get('/api/posts').map(response => response.json()).subscribe(data => {
  var items = data.items
  var total = data.total
});

But I don't like this as I cannot use my interface for the Post entity (or I must defined an interface like that {items: Post, total: number} this is not really graceful). What I would like to do is something like this :

this.myPostService.totalObservable.subscribe(total => this.total = total)
this.myPostService.itemsObservable.subscribe(items => this.items = items)

But without triggering 2 queries.

I looked into this blog post : https://coryrylan.com/blog/angular-2-observable-data-services but I don't like the fact that you first subscribe and then call the list() method to load the list.

Upvotes: 1

Views: 1289

Answers (1)

paulpdaniels
paulpdaniels

Reputation: 18663

If you only need it to run once you can cache the value directly:

var source = this.http.get('/api/posts').map(response => response.json()).publishLast();

//unsubscribe from this when you are done
var subscription = source.connect();

Then you can expose the two sources as:

var totalObservable = source.pluck('total');
var itemsObservable = source.pluck('items');

If on the other hand you need to call these Observables multiple times, then I would recommend that you wrap the get in a triggering Observable and cache:

var source = sourceTrigger
  .startWith(0) //Optionally initialize with a starting value
  //This ensures that only the latest event is in flight.
  .flatMapLatest(() => this.http.get('/api/posts'), (_, resp) => resp.json())
  .cache(1);

//Again expose these items using `pluck`
var totalObservable = source.pluck('total');
var itemsObservable = source.pluck('items');

In the second case the sourceTrigger would be an Observable that might be connected to a button event, so that each button click triggers a new request.

Upvotes: 3

Related Questions