Wrap a LoadingController around observables

Hello everyone,

I’m trying to use a LoadingController to show an indicator while I’m doing calls to a remote service, but I have some troubles.

My current situation is the following. I have a PageComponent with a simple loading method, something like:

export class PageComponent {

  constructor(private itemsService: ItemsService) { }

  fillData() {
    this.itemsService.getItems().subscribe(items => {
      this.items = items;
    });
  }

}

The ItemsService is simple too:

export class ItemsService {

  constructor(private http: HttpClient) { }

  getItems() {
    let url = 'http://example.org/items/endpoint';

    return this.http.get(url).pipe(
      map(this.buildItems.bind(this)
    );
  }

  private buildItems(items: any[]) {
    // convert remote data format to internal one
  }
}

Now I’d like to put a LoadingController between the twos, by making both the caller and the callee basically unaware of what’s really happening.

So my idea is to create an intermediate service class that’s called from the page class. To make it flexible enough to be used with any kind of service (I have more than ItemsService), I tried to pass to the intermediate class the same Observable I got from the ItemsService like this:

(in PageComponent)

fillData() {
  this.loadingService.loadData(this.itemsService.getItems()).subscribe(items => {
    this.items = items;
  });
}

and I defined a new service like this:

export class LoadingService {

  loadData(observable: Observable<any>): Observable<any> {
    return observable.pipe(
      finalize(() => {
        console.log('after data received');
      })
    );
  }

}

This code seems to work. When I call the fillData in PageComponent, I get the observable from ItemsService, add a finalize() operator to it and finally (in fillData()) I subscribe the observable, having the real remote request called.

This way, if I have many services shaped like ItemsService, I can do

this.loadingService.loadData(this.service1.getData()).subscribe( /* ... */ );
this.loadingService.loadData(this.service2.getData()).subscribe( /* ... */ );
this.loadingService.loadData(this.service3.getData()).subscribe( /* ... */ );
/* ... and so on */

However, as soon as I add the LoadingController in LoadingService, things go weird. This is how I changed the service:

export class LoadingService {
  private loader = null;

  constructor(private loadingController: LoadingController) { }

  loadData(observable: Observable<any>): Observable<any> {
    this.openLoader();
    return observable.pipe(
      finalize(() => {
        this.closeLoader();
      })
    );
  }

  private async openLoader() {
    if (! this.loader) {
        this.loader = await.loadingController.create();
    }
    await this.loader.present();
  }

  private async closeLoader() {
    if (this.loader) {
      await this.loader.dismiss();
      this.loader = null;
    }
  }
}

Now what I get is that the loading controller actually presents the indicator and usually does not dismiss it after the remote call ends. Sometimes, however, I get the correct behavior: the indicator shows, the call starts, the call ends, the indicator goes away.

I’m not very confident with event-based programming in JS, so it is probably a very stupid question, but I need some help to solve this problem.

Thanks a lot,

Marco