Dismiss Loader on Page Change in Ionic 4
In Ionic 3, there was an easy way to automatically dismiss a loading overlay when the navigation to a new page completed - you only had to set the dismissOnPageChange
flag when creating the overlay:
const loader = this.loadingCtrl.create({
content: "Loading...",
dismissOnPageChange: true
});
There's no such flag available in Ionic 4:
const loader = await this.loadingCtrl.create({
message: "Loading..."
// dismissOnPageChange: true - not supported
});
This is documented in the list of breaking changes. However, the whole section about it is useless at the moment for the following reasons:
- There are no
ionNavWillChange
andionNavDidChange
events raised by theIonRouterOutlet
which is used by default in all templates for Angular-based projects. - The sample code suggested to be used in Ionic 4 instead of the missing
dismissOnPageChange
flag doesn't provide the same functionality:
async openLoading() {
let loading = this.loadingCtrl.create({
message: 'Loading...'
});
await loading.present();
const { role, data } = await loading.onDidDismiss();
console.log('Loading dismissed!');
}
For new Ionic 4 projects, it's probably best to just forget about the missing functionality and always manually dismiss the loading overlay by invoking its dismiss
method. However, when porting large Ionic 3 applications to Ionic 4 this can involve a lot of code changes and introduce new bugs.
To avoid that, I decided to create my own helper method for creating the loading overlay which still accepts the dismissOnPageChange
flag:
async createLoadingOverlay(
opts?: LoadingOptions & { dismissOnPageChange?: boolean }
): Promise<HTMLIonLoadingElement> {
const loader = await this.loadingCtrl.create(opts);
if (opts.dismissOnPageChange) {
loader.addEventListener('ionLoadingDidPresent', () => {
loader.setAttribute('dismissOnPageChange', 'true');
});
}
return loader;
}
As you can see, I decided to store the value of the flag as an attribute of the loading element where I can inspect it when the page navigation occurs and act accordingly. Notice also, that I'm waiting for the ionLoadingDidPresent
event to be raised for the element before setting the flag. Without this check it could happen that the loading overlay was flagged for dismissal before it was ever presented if it was created during the initialization of the page (e.g. in the ionViewDidEnter
lifecycle method).
I still had to find the alternative to the non-existent ionNavWillChange
and ionNavDidChange
events. By exploring the IonRouterOutlet
source code, I found the non-documented activate
and deactivate
events which seemed similar enough. I hooked a handler to the first one which inspects the top-most loading overlay and dismisses it if it was created with the dismissOnPageChange
flag:
<ion-router-outlet (activate)="onPageActivated()"></ion-router-outlet>
async onPageActivated() {
const topLoader = await this.loadingCtrl.getTop();
if (topLoader && topLoader.getAttribute('dismissOnPageChange') === 'true') {
await topLoader.dismiss();
}
}
Based on the initial tests, this implementation works reliably enough. Of course, it won't automatically work with nested outlets (e.g. with tab pages). If necessary, similar hooks can be added to those as well. It also won't dismiss the non-top loading overlays if more than one is open at the same time. Hopefully you're not doing that.