I used \"Ionic Loading Controller\" to show a spinner until the data is retrieved then it calls \"dismiss()\" to dismissed it. it works fine, but sometimes when the app alre
CHECK THIS OUT!
After reading through these solutions I have come up with a solution that prevents loaders from stacking etc. It works great!
import { Injectable } from '@angular/core';
import { LoadingController } from '@ionic/angular';
import { LoadingOptions } from '@ionic/core';
import { TranslateService } from '@ngx-translate/core';
import { isNil } from 'lodash-es';
import { BehaviorSubject } from 'rxjs';
import { filter } from 'rxjs/operators';
enum LoadingTypeEnum {
show,
hide,
message,
}
@Injectable({
providedIn: 'root',
})
export class LoadingService {
/**
* this is a special behavior subject we can use on an inital load to show or hide a background etc.
* EXAMPLE: on inital profile load, we might want to have ngIf on an overlay and simply listen for this event.
*/
public appLoaded$ = new BehaviorSubject<boolean>(false);
public loading$: BehaviorSubject<{ type: LoadingTypeEnum; data?: any }> = new BehaviorSubject<any>({ type: LoadingTypeEnum.hide });
loadingState: { type: LoadingTypeEnum; data?: any } = null;
public loading: HTMLIonLoadingElement = null;
public loaderLoaded = false;
public i;
public spinningUp = false;
constructor(private loadingController: LoadingController, private translate: TranslateService) {
const l$ = this.loading$.pipe();
l$.pipe(filter((l) => l.type === LoadingTypeEnum.show)).subscribe((l) => this.showLoading(l.data));
l$.pipe(filter((l) => l.type === LoadingTypeEnum.hide)).subscribe(() => this.hideLoading());
}
show(opts?: LoadingOptions) {
if (isNil(opts)) {
opts = {
message: 'Please wait...', // this.translate.instant('PLEASE_WAIT'),
};
}
this.loading$.next({ type: LoadingTypeEnum.show, data: opts });
}
hide() {
this.loading$.next({ type: LoadingTypeEnum.hide });
}
message(m: string) {
this.loading$.next({ type: LoadingTypeEnum.message, data: m });
}
private async showLoading(opts: LoadingOptions) {
if (!this.loading && !this.spinningUp) {
this.spinningUp = true;
this.loading = await this.loadingController.create(opts);
await this.loading.present();
this.spinningUp = false;
}
}
private async hideLoading() {
const t = setTimeout(() => {
if (this.loading && !this.spinningUp) {
this.loading.dismiss().then(() => {
this.loading = null;
this.spinningUp = false;
clearTimeout(t);
});
}
}, 1000);
}
}
same problem here, and here my solution (ionic 4 and angular 7):
Started from the acepted solution.
the present creates the loading one time In the dismiss function, i set isShowing to false only if dimiss returns true
import { Injectable } from '@angular/core';
import { LoadingController } from '@ionic/angular';
@Injectable({
providedIn: 'root'
})
export class LoadingService {
isDismissing: boolean;
isShowing: boolean;
constructor(public loadingController: LoadingController) {
}
async present() {
if(this.isShowing){
return
}
this.isShowing = true
await this.loadingController.create({spinner: "dots"}).then(re => {
re.present()
console.log("LoadingService presented", re.id)
})
}
async dismiss() {
if(this.isShowing){
await this.loadingController.dismiss().then(res => {
if(res){
this.isShowing = false
console.log("LoadingService dismissed", res);
}
})
}
}
}
Alternatively, you have to change the code where call loading like below
async ngOnInit() {
const loading = await this.loadingController.create();
await loading.present();
this.customerService.getCustomer('1')
.subscribe(customer => {
this.customer = customer;
loading.dismiss();
}
}
While the accepted solution can work... I think it is better to just have 1 loading always. My solution dismisses a previous loading, should it exist, and creates the new one. I usually only want to display 1 loading at a time (my particular usecase), so this solution works for me.
The accepted solution poses the problem of possible orphaned loadings. But it is a good starting point
for Ionic 4 check this solution
Source Link
import { Component } from '@angular/core';
import { LoadingController } from '@ionic/angular';
@Component({
selector: 'app-home',
templateUrl: 'home.page.html',
styleUrls: ['home.page.scss'],
})
export class HomePage {
loaderToShow: any;
constructor(
public loadingController: LoadingController
) {
}
showAutoHideLoader() {
this.loadingController.create({
message: 'This Loader Will Auto Hide in 2 Seconds',
duration: 20000
}).then((res) => {
res.present();
res.onDidDismiss().then((dis) => {
console.log('Loading dismissed! after 2 Seconds');
});
});
}
showLoader() {
this.loaderToShow = this.loadingController.create({
message: 'This Loader will Not AutoHide'
}).then((res) => {
res.present();
res.onDidDismiss().then((dis) => {
console.log('Loading dismissed! after 2 Seconds');
});
});
this.hideLoader();
}
hideLoader() {
setTimeout(() => {
this.loadingController.dismiss();
}, 4000);
}
}
I was facing the same issue, maybe I have an easier and more reliable solution using ionic events itself. This worked for me. It will wait until the loader is created and only then the service call will be done, and only when the service call is complete, only then the loader is dismissed. I hope this helps..
yourFuncWithLoaderAndServiceCall(){
this.presentLoading().then(()=>{
this.xyzService.getData(this.ipObj).subscribe(
res => {
this.dismissLoading();
this.dismissLoading().then(() => {
this.responseObj = res;
})
}
});
}
async presentLoading() {
this.loader = await this.loadingController.create({
translucent: true
});
await this.loader.present();
}
async dismissLoading() {
await this.loader.dismiss();
}