Why LoadingController throws 'Overlay does not exist ' when trying to dismiss it during an API call

Viewed 300

I have a created a service for an custom loading controller with the following methods:

    async showLoader(){
        //Show cutom loader
        this.loadingController.create({
          message: 'Loading...',
          cssClass: 'loader-css-class'
        }).then((res) => {
          return res.present();
        });
      }
     
      async dismissLoader(){
        //Hide cutome loder
        this.loadingController.dismiss().then((response) =>{
          console.log("Loader closed: ", response);
        }).catch((err) => {
          console.log('Errro occured closing loader: ' + err);
        });
     }

The service is used and load controller called when prior to an API request is made, and dismissed when the API call returned data :

    constructor(
       public loadingController : LoadControllerServiceService){}

The API call is made from ngOnInIt:

ngOnInit(){

    this.getData();
   
 }

This method makes an API call :

    getData(){
       //get data form remote serve using service
       console.log("conatct.ts: get all conatcts");
      
       this.loadingController.showLoader();
       this.service.getAll().subscribe(data => {
        
         this.allDepartments = data;
         this.allDepartmentsOriginal = this.allDepartments;
         this.loadingController.dismissLoader();
    
         //itearte array and assign bsepearte departmental contacts 
       console.log("contacts.page.ts: trting to get biohemistry  data...");
        for (var contact of this.allDepartments){
         ...
         ...
        }
    
    }

I have looked at this answer and various other answers but can't seem to get load / dismiss right as I keep getting the exception: overlay does not exist.

Any input appreciated.

2 Answers

The "overlay doesn't exist" error happens when you dismiss the loader before presenting it, your code seems correct, but to make sure, subscribe to the getAll() inside the service and return the data in a promise so your code should be:

getData(){
       //get data form remote serve using service
       console.log("conatct.ts: get all conatcts");
      
       this.loadingController.showLoader();
       this.service.getAll().then((data:any) => {
        
         this.allDepartments = data;
         this.allDepartmentsOriginal = this.allDepartments;
         this.loadingController.dismissLoader();
    
         //itearte array and assign bsepearte departmental contacts 
       console.log("contacts.page.ts: trting to get biohemistry  data...");
        for (var contact of this.allDepartments){
         ...
         ...
        }
    
    }

The problem that u have met is async issue, loadingController.create races with getData, they are both async.

First u should write u functions, that will return obs. for futher chaining

function showLoader() {
  //Show custom loader
  return from(this.loadingController.create({
    message: 'Loading...',
    cssClass: 'loader-css-class'
  })).pipe(map(res => res.present));
}

function dismissLoader() {
  this.loadingController.dismiss()
}

Than u should chain u function calls to pipe

function getData(){
  //get data form remote serve using service
  console.log("conatct.ts: get all conatcts");

  this.showLoader().pipe(
    switchMap(_ => {
      return this.service.getAll()
    }),
    finalize(() => this.dismissLoader())
  ).subscribe(data => {
    this.allDepartments = data;
    this.allDepartmentsOriginal = this.allDepartments;
  });
}

ps.

  1. It's bad practice to mix rxjs and promises, u should transform promise calls to observables.
  2. In angular for proper detect change and other stuff, u should use rxjs
Related