0
votes

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?

1
What are you trying to achieve? What is the setTimeout for? What's wrong with return this.http.get(this.services_URL).map(res => res.json());?JB Nizet
The timeout is for nothing really, it's just the only tutorial I found that explained how to actually set an Observable's value had that in there. The problem would be that I don't want it to call the service each timeJoao Ventura

1 Answers

3
votes

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.