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?
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)
}
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();
}
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.