Reputation: 65
I am trying to clean-up a project of mine with a bunch of duplicated code, I call my back-end a bunch of times from multiple components which really slows down my program.
Previously each component, inside the ngOnInit() method, had something like this in it in order to initialize an array of services:
this.http.get(this.ds.services_URL).map((res)=>res.json()).subscribe(
(data) =>{
this.Services=data;
});
What I wanted to do though is having one service that makes the http call once and then my components can request my service for the data.
I've tried having something like this in my Service:
private loadServices()
{
this.http.get(this.services_URL).map((res)=>res.json()).subscribe(
(data) =>{
this.serviceO=new Observable(observer=>{
setTimeout(()=>{
observer.next(data);
observer.complete();
},1000);
});
});
}
serviceO being an Observable, the different components would subscribe to it rather than the http.get result.
The problem here being (besides the fact that I am not sure of how to actually set an Observable's value), that we are actually redefining the entire Observable asynchronously, so it is actually impossible for the Observer to have a reference to the observable (I get "Cannot read property 'subscribe' of undefined" kind of exception)
Seeing the problem I am facing, I think I am not going the right direction, so does anyone know a better direction on how to achieve what I want to achieve?
Upvotes: 0
Views: 1853
Reputation: 13416
I approach this by sharing an observable and storing the results within the service. Whenever a component needs that data, it can used the stored result instead of making a new request (https://plnkr.co/edit/M9ZIViYhSbKzPlzKXC7H)
export class Service {
observableShare: Observable<string>; // store the shared observable
observableResult: string; // store the result of the observable when complete
constructor(private http: Http){}
getData(){
if(this.observableResult){ // if result has already been found (api has completed) return result
// create dummy observable and return results
return new Observable<string>(observer => {
observer.next(this.observableResult);
observer.complete();
})
}else if(this.observableShare){ // else if api is still processing, return shared observable
return this.observableShare;
}else{ // else api has not started, start api and return shared observable
this.observableShare = this.http.get('url')
.map(res => {
this.observableResult = res; // store result in service
console.log('observable sequence', res); // note, this only runs once even though there are 3 separate subscribes
return res;
})
.share(); // share observable sequence among multiple subscribers
return this.observableShare;
}
}
}
So what is happening here is the first component to call getData() initiates the http request. If another component needs that data before the request finishes, it is handed the shared observable to listen to. This ensures that multiple http requests (for the same data) are not fired. And lastly, if a component calls getData() and the http request has already finished, getData() will create an observable with the stored value. Now multiple components can subscribe to the observable with only a single http request.
Upvotes: 3