62

I've created a demo (ng-run) where I have a button which invokes an Http request.

When the button is clicked, I invoke this method :

public getData(){
 this._jokeService.getData().subscribe();
}

Which in turn invokes this ( from a service) :

 public getData() {
    return this.http.get(API_ENDPOINT).pipe(shareReplay(1))
  }

The problem is that on every click - I still see a new http request initiated :

enter image description here

Question:

Why doesn't shareReplay keeps the last value of the response?
How can I make my code to invoke the http only once and keep that value for future subscriptions ?

Edit: solution is here

Royi Namir
  • 144,742
  • 138
  • 468
  • 792
  • 8
    Why *wouldn't* you see a new request? You're calling `http.get` again! The `shareReplay` prevents additional subscribers to the returned observable triggering a new response, but you're creating a new one each time. If you want to make a single request then expose the existing value to future subscribers use a `ReplaySubject` as I show here: https://stackoverflow.com/a/41554338/3001761. Then you can differentiate between triggering a new request and asking for the existing value, *plus* if you do need to make an additional request all existing subscribers receive the new result when available. – jonrsharpe Jun 26 '18 at 13:46
  • I don't think I need to create a middle `ReplaySubject` just to keep those values. IMHO - it can be done without it. – Royi Namir Jun 26 '18 at 13:49
  • Yes, it can be done without it, as long as you only ever want one value back. But you'll still need to store and expose a single observable, *not* create a new one each time. – jonrsharpe Jun 26 '18 at 13:50
  • Yeah silly mistake of mine . I've created each time a new observable ( happens) – Royi Namir Jun 26 '18 at 13:58
  • I have written a library ngx-RxCache https://github.com/adriandavidbrand/ngx-rxcache to help you manage thing like this in Angular. Have a read about it here https://medium.com/@adrianbrand/angular-state-management-with-rxcache-468a865fc3fb – Adrian Brand Oct 28 '19 at 21:57

2 Answers2

68

If you call every time this.http.get(API_ENDPOINT).pipe(shareReplay(1)), each time http request will be triggered. If you want to make http call once and cache data, the following is recommended.

You first get the observable for data:

 ngOninit(){
    this.data$ =  this._jokeService.getData().pipe(shareReplay(1));
    }

Now subscribe multiple times:

 public getData(){
     this.data$.subscribe();
    }

Your service:

public getData() {
    return this.http.get(API_ENDPOINT)
  }
siva636
  • 16,109
  • 23
  • 97
  • 135
  • 7
    Yeah , I've created a new observable each time..... that was my mistake. Tnx.). It's been a long day... – Royi Namir Jun 26 '18 at 13:55
  • 6
    @siva636 I understand what you are doing here but can you tell me the benefits of your approach over simply piping the http.get in your service and extracting the data and putting it in a non observable variable in the service? – lolplayer101 Oct 18 '19 at 12:35
  • This can be simplified to `shareReplay({bufferSize:1, refCount: false})` – Patrick Nov 14 '22 at 14:37
9

In service:

getData$ = this.http.get(API_ENDPOINT).pipe(shareReplay(1));

In component, need to unsubscribe and you can subscribe multiple times with single API call:

ngOninit(){
   this.data$ =  this._jokeService.getData$;
   this.data$.subscribe() 
}

In template, use:

*ngIf="data$ | async as data"
Das_Geek
  • 2,775
  • 7
  • 20
  • 26
Arunsai B K
  • 193
  • 1
  • 12
  • no need to unsubscibe for http request, Finite Observables are ones that will stop emitting values at some point before the application ends, whereas infinite ones will not stop until the end of time (when you close your browser tab).see this link https://www.seangwright.me/blog/development/unsubscribe-angular-2-http-observables/ – moh Dec 21 '22 at 09:54