5

I have a shared service (starting from this suggestion) that cache and returns some data after first http request:

export class SharedService {
  constructor(private http:Http) {
  }

  getData() {
    if (this.cachedData) {
      return Observable.of(this.cachedData);
    } else {
      return this.http.get(...)
            .map(res => res.json())
            .do((data) => {
              this.cachedData = data;
            });
    }
  }
}

My problem is that I have some directives and components inside the same template that are all initialized at the same time and each one call simultaneously (inside ngInit function) the getData method (all of them before the first one succeeded) and so the service starts many http requests instead of returning cached data. Can someone suggest me how to avoid this side effect?

Community
  • 1
  • 1
Massimo Magliani
  • 649
  • 2
  • 7
  • 17

2 Answers2

4

Your solution doesn't cover all cases.

Compare it to my approach from https://stackoverflow.com/a/36291681/217408

getData() {
    if(this.data) {
      // if `data` is available just return it as `Observable`
      return Observable.of(this.data); 
    else if(this.observable) {
      // if `this.observable` is set then the request is in progress
      // return the `Observable` for the ongoing request
      return this.observable;
    } else {
      // create the request, store the `Observable` for subsequent subscribers
      this.observable = this.http.get('/someUrl')
          .map(res => res.json())
          .do(val => {
            this.data = val;
            // when the cached data is available we don't need the `Observable` reference anymore
            this.observable = null;
          })
          // make it shared so more than one subscriber can get the result
          .share();
      return this.observable;
    }
}

Until the request hasn't returned you need to return the Observable that was created from the first request to subsequent requests until the first requests completed.

https://stackoverflow.com/a/36296015/217408 also shows and interesting approach but with the small (depending on your requirments) drawback that requests can't be cancelled.

Also ensure you have registered your shared service only once as provider like explained by @MichaelD.

The Angular team recommends using the providers: [...] list of the root component instead of bootstrap(...) but there are no technical reasons. They consider it a bit more maintainable.

Community
  • 1
  • 1
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
1

You have to declare your service in the bootstrap method:

bootstrap(AppComponent, [SharedService]);

The service will be instanciated only once (singleton) so it should resolve your problem.

Michael Desigaud
  • 2,115
  • 1
  • 15
  • 15