Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to reuse angular observable across multiple components? (To avoid having to duplicate a request twice?)

I have a service "MyHttpService" that includes an observable like this:

grabData() {
return this.http.get('myaddress')
                         .map((res:Response) => {return res.json()})
                         .catch((error:any) => Observable.throw(error.json().error || 'Server error')); 
}

I have 2 components. OneComponent and TwoComponent that both have "MyHttpService" injected that depend on the data returned by MyHttpService.

OneComponent loads up first. TwoComponent loads up after button click.

Within the onNgInit() of each component I have:

     this.myHttpService.grabData()
        .subscribe(
              data => {
 // do something to the data
});

Is it correct to assume that even though I have this subscribe in both components, that there are not going to be multiple HTTP requests called and that the call to "grabData()" when TwoComponent loads is the same data already pulled by OneComponent? Or will it make a new call? I want to avoid multiple HTTP requests to the same endpoint. If multiple calls are being made each time a component that has this is called, what is the best way to handle this so that I don't have multiple calls to the service each time I initialize TwoComponent?

like image 703
Rolando Avatar asked Jul 19 '17 21:07

Rolando


3 Answers

The Http service returns what are known as cold observables. This means that every new subscriber will cause the work for that observable to be done again, which in the case of Http, is to make the network request.

Luckily, there are mechanisms in RxJS to allow you to publish (a.k.a multicast or multiplex) an observable so that multiple subscribers do not cause multiple requests.

The way I usually do this for Http is with the .publishReplay(1).refCount() pair of operators. The .publishReplay(1) means that later subscribers get the most recent successful value straight away, without making another request. .refCount() means that the first subscriber makes the request, and the last one to unsubscribe cleans up the original Http observable.

Version 5.4.0 added a shortcut for these two operators, which is .shareReplay(1).

Just whack either version on the end of the line in your grabData() service method, and it should work like you desire.

grabData() {
    return this.http.get('myaddress')
                    .map((res:Response) => {return res.json()})
                    .catch((error:any) => 
                         Observable.throw(error.json().error || 'Server error'))
                    .publishReplay(1).refCount(); // or .shareReplay(1)
}
like image 110
GregL Avatar answered Nov 14 '22 03:11

GregL


You can use the share operator to make an observable multicast. This is also relevant within a single component as multiple async bindings to the same osbervable will cause multiple requests to be made. There is a good ng-conf talk here that covers the issue.

import 'rxjs/add/operator/share';

grabData() {
  return this.http.get('myaddress')
                  .map((res:Response) => {return res.json()})
                  .catch((error:any) => Observable.throw(error.json().error || 'Server error'))
                  .share(); 
}
like image 4
JayChase Avatar answered Nov 14 '22 03:11

JayChase


When you subscribe in TwoComponent it will cause the http request to be sent again. The best way for you to prevent multiple http calls would be to store the response data in your service and before the http call, check if you have already saved the data and if so just directly returning the data.

like image 3
SimplyComplexable Avatar answered Nov 14 '22 03:11

SimplyComplexable