2

In my Angular2 set up I've got a component that calls a service method. The service method returns an Observable and the component subscribes to it.

As I want the service to cache recent calls, I'd intercept the the data in the Observable with the map function. The exemplary service method looks like this

provideData():Observable<Data> {
  return this.http.get(url).
    map(resp => resp.json()).
    map(data => { 
          this.cachedData = _.cloneDeep(data);
          return data;
    }
}

This approach works for me, however, I'm unsure if I don't abuse the map function which doesn't map anything. It's just an interceptor to lay hold of the data. Are there better or more recommended ways?

PS.: This is not necessarily about caching, rather than the appropriate usage of the map function for non-mapping operations. In this question Günter's widely approved answer does actually the same thing and uses map for that. On the other hand, the user olsn stated in the comments that do would be even more appropriate.

Community
  • 1
  • 1
Jan B.
  • 6,030
  • 5
  • 32
  • 53
  • 1
    What about http://stackoverflow.com/questions/36271899/what-is-the-correct-way-to-share-the-result-of-an-angular-2-http-network-call-in/36291681#36291681 – Günter Zöchbauer Feb 04 '17 at 19:02
  • @GünterZöchbauer Good to see that you basically did the same thing in terms of leveraging the map function to perform the caching. It seems that this is the way to go then. – Jan B. Feb 04 '17 at 19:12
  • 1
    Instead of use `map`, use the `do`-operator, that way you don't have to add an return-statement – olsn Feb 04 '17 at 19:12
  • @olsn *that's* what I have been looking for! I didn't really search for a way to perform caching, it was just about the usage of the map function for actually non-mapping operations. You might like to put that into an answer? – Jan B. Feb 04 '17 at 19:19
  • 1
    since you where asking for "caching data" i'd rather not see this as a "valid answer" because it is not a very good practice, when you want to cache data, you should go with the `replayLast().refCount()` – olsn Feb 04 '17 at 19:22
  • 1
    I haven't time to post the answer before the question was marked as duplicate. But yes, `do` should be used for what you're asking for. And no, it certainly shouldn't be used in this case, because it is XY problem. `cachedData` is the sign that a developer isn't comfortable enough with observables. The problem can be solved in idiomatic way, as it was shown in dupe question. – Estus Flask Feb 04 '17 at 19:22
  • @olsn and @estus I see your points and I agree as far it is just about pure caching. There is a case where I've got an object that is expensive to compute in the backend, so for primitive updates I'd just send a request to the backend with a void return and edit the cached object directly. Whatever, I'm happy with the ``do`` function and will read up on your proposals with ``replaying`` data. Thanks so far. – Jan B. Feb 04 '17 at 19:48

1 Answers1

1

PublishReplay makes your stream multicast compatible and caches the source stream.

provideData():Observable<Data> {
  return this.http.get(url)
    .map(resp => resp.json())
    .publishReplay();
}

For a extensive explanation how this operator works have a look at Making a lazy, cached observable that only execute the source once

Mark van Straten
  • 9,287
  • 3
  • 38
  • 57
  • Hey Mark. Thanks for your answer. As discussed in the question's thread I was looking for something like the ``do`` function. But, obviously, especially for caching purposes, your suggestion is absolutely valid. I'll read up on that. – Jan B. Feb 04 '17 at 19:51