[ionic4] Dismiss Loading spinner among pages

#1

I’m building an app in which I have several pages which I can navigate to (always trough routing, as suggested on new ionic 4 rather than with navController).

My initial idea was to handle this from my AppComponent which is the bootstrap component which is always loaded and present/dismiss the loading spinner on routing navigations by listening to Angular Route Events. I’ve found out that ionic lifecycle hooks are triggered always after the NavigationEnd event of Angular router, so it is not possible to handle this from these events.

I’ve also found out that the option for a new loading spinner to be presented dismissOnPageChange is not available anymore, which initially seemed really handy since we would expect the loader to automatically hide once the new page that has been navigated to, is shown.

Then I’ve tried to define a service provided in root which all page components could trigger methods either to show a loading spinner, or hide a loading spinner depending on the lifecycle hook method. Been playing around with ionViewWillEnter/ ionViewWillLeave and ionViewDidEnter/ionViewDidLeave. The problem with this is that the order of the methods triggered are not always the expected ones, if I’m navigating from page1 to page2 I would expect always for the methods ionViewWillLeave/ionViewDidLeave from page1 to trigger first and then the methods ionViewWillEnter/ionViewDidEnter from page2 afterwards. This doesn’t happen so the behaviour is not the expected one.

Currently I’m mixing the approaches, I’am listening for the NavigationEnd event at the AppComponent for showing the loading spinner. And I’m hiding it on the different pages when the methods ionViewWillEnter/ionViewDidEnter are trigger. Still the result seems buggy from page to page.

The best current approach I’ve found so far is using something like:

async navigate() {
  const loading = await this.loadingController.create({});
  await loading.present();
  await this.router.navigate(['/your-route']);
  await loading.dimiss();
}

Based on this post Ionic 4 dismissOnPageChange (but instead of doing it with the navController, doing it with the router itself). The problem with this is that by doing so, I’ve always forced to navigate to pages programmatically, and this is not always desired. I believe the framework should provide a more easier ‘out-of-the-box’ approach for achieving this kind of behavior.

Does anyone has an idea about how this can be done?
Thanks in advance

#2

@rodrigojrmartinez Did you find an answer? I’m also looking for something similar…

#3

Yes, what I ended up doing was to arrange a service with root scope that may handle the loading spinner, either to show it or to hide it.

Both showing and hiding the spinner are functions that return promises. So ideally you would have to wait until the spinner actually showed before attempt hiding it.

so, what I do, is to provide a function for showing the spinner. and it will return an observable for any component from which you’d like to trigger a spinner would use. Something like;

public showSpinner(spinnerOptions): Observable<void> {
        // will return the result of this operations that starts by creating the loading spinner
        return from(this.loadingController.create(spinnerOptions)).switchMap(
            (spinner: HTMLIonLoadingElement) => {
                // keep track of the current spinner instance for hiding it afterwards
                this.spinner = spinner;
                // and return just after the spinner actually presented over the page
                return from(this.spinner.present());
            }
        );
    }

In your component, you would like to do something like

public action(){
       //any action you would like to perform, then
       this.spinnerService.showSpinner(someOptions).subscribe( ()=> {
            //at this point the spinner is already been shown, so you can now route with no worries
             this.router.navigate(['/your-route']);
       });
}

Afterwards, when your route component initializes after navigating, you could hide it over an init method, something like

ionViewWillEnter(){
    // now that we have navigated and do some init actions we can do
    this.spinnerService.hideSpinner().subscribe(() => {
         //now the spinner has been hidden, and you can do some extra stuff at this point if you would need to
    });
}

At last, over the spinner service the hide function would be something like

public hideSpinner(): Observable<boolean | void>{
    // check for the previously created spinner
    if(this.spinner){
         // if this is the case, then hide the spinner for that instance
         return from(this.spinner.dismiss());
    } else {
        // If by any reason you don't have the spinner instance you could try to dismiss any existent spinner by the use of the loadingController
         return from(this.loadingController.dismiss());
    }
}

If you plan to always navigate among pages like these, an alternative would be to listen for the NavigationEnd event from the angular’s router at one place and when this fires, invoke the hideSpinner method, but this may have its perks

Hope it helps!