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 already have the data, the "dismiss()" is called before the "create()" and "present()" is done which will keep the spinner without dismissing...
I tried to call the data inside "loadingController.present().then()", but that caused the data to be slower...
is this a bug? how to solve the this issue?
Example of my code:
customer: any;
constructor(public loadingController: LoadingController, private customerService: CustomerService)
ngOnInit() {
this.presentLoading().then(a => consloe.log('presented'));
this.customerService.getCustomer('1')
.subscribe(customer => {
this.customer = customer;
this.loadingController.dismiss().then(a => console.log('dismissed'));
}
}
async presentLoading() {
const loading = await this.loadingController.create({
message: 'wait. . .',
duration: 5000
});
return await loading.present();
}
this is how I solved my issue..
I used a boolean variable "isLoading" to change to false when dismiss() is called. after present() is finished if "isLoading" === false (means dismiss() already called) then it will dismiss immediately.
also, I wrote the code in a service so I don't have to write it again in each page.
loading.service.ts
import { Injectable } from '@angular/core';
import { LoadingController } from '@ionic/angular';
@Injectable({
providedIn: 'root'
})
export class LoadingService {
isLoading = false;
constructor(public loadingController: LoadingController) { }
async present() {
this.isLoading = true;
return await this.loadingController.create({
duration: 5000,
}).then(a => {
a.present().then(() => {
console.log('presented');
if (!this.isLoading) {
a.dismiss().then(() => console.log('abort presenting'));
}
});
});
}
async dismiss() {
this.isLoading = false;
return await this.loadingController.dismiss().then(() => console.log('dismissed'));
}
}
then just call present() and dismiss() from the page.
the example in question:
customer: any;
constructor(public loading: LoadingService, private customerService: CustomerService)
ngOnInit() {
this.loading.present();
this.customerService.getCustomer('1')
.subscribe(
customer => {
this.customer = customer;
this.loading.dismiss();
},
error => {
console.log(error);
this.loading.dismiss();
}
);
note: don't forget to add "LoadingService" to AppModule's providers
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);
}
}
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 😊.
Therefore, this is my proposed injectable service (you can overcharge it with more ionic settings, if you need it. I didn't need them so I didn't add more in the present function, but they can be added accordingly):
import {Injectable} from '@angular/core';
import {LoadingController} from '@ionic/angular';
@Injectable({
providedIn: 'root'
})
export class LoadingService {
currentLoading = null;
constructor(public loadingController: LoadingController) {
}
async present(message: string = null, duration: number = null) {
// Dismiss previously created loading
if (this.currentLoading != null) {
this.currentLoading.dismiss();
}
this.currentLoading = await this.loadingController.create({
duration: duration,
message: message
});
return await this.currentLoading.present();
}
async dismiss() {
if (this.currentLoading != null) {
await this.loadingController.dismiss();
this.currentLoading = null;
}
return;
}
}
The simple way is Add setTimeOut function :
setTimeout(() => {
this.loading.dismiss();
}, 2000);
I'm using a similar solution but relying on the Ids of the loading overlays and letting the Ionic Loading Controller manage what overlay should be on top.
LoadingService
import { Injectable } from '@angular/core';
import { LoadingController } from '@ionic/angular';
@Injectable({
providedIn: 'root'
})
export class LoadingService {
constructor(public loadingController: LoadingController) { }
async present(loadingId: string, loadingMessage: string) {
const loading = await this.loadingController.create({
id: loadingId,
message: loadingMessage
});
return await loading.present();
}
async dismiss(loadingId: string) {
return await this.loadingController.dismiss(null, null, loadingId);
}
}
Components/Services using the LoadingService
import { LoadingService } from '../loading/loading.service';
@Injectable({
providedIn: 'root'
})
export class MessagesService {
...
constructor(
protected http: HttpClient,
protected loading: LoadingService
) { }
...
protected async loadMessagesOverview() {
const operationUrl = '/v1/messages/overview';
await this.loading.present('messagesService.loadMessagesOverview', 'Loading messages...');
this.http.get(environment.apiUrl + operationUrl)
.subscribe((data: Result) => {
...
this.loading.dismiss('messagesService.loadMessagesOverview');
}, error => {
...
this.loading.dismiss('messagesService.loadMessagesOverview');
console.log('Error getting messages', error);
});
}
}
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();
}
I had the same problem, apparently I figured It out by identifying the problem first. This problem occurs when the duration of that Loader got expired so it basically got dismissed without our full control..
Now, It will work fine Until you are also using dismiss()
MANUALLY.
So if you are going to use dismiss()
manually with duration, remove the duration on create. then use setTimeout()
perhaps
// create loader
this.loader = this.loadingCtrl.create()
// show loader
this.loader.present().then(() => {})
// add duration here
this.loaderTimeout = setTimeout(() => {
this.hideLoader()
}, 10000)
then create your hide loader here
// prepare to hide loader manually
hideLoader() {
if (this.loader != null) {
this.loader.dismiss();
this.loader = null
}
// cancel any timeout of the current loader
if (this.loaderTimeout) {
clearTimeout(this.loaderTimeout)
this.loaderTimeout = null
}
}
This onDidDismiss() event should created after .present() function called.
Example:
this.loader.present().then(() => {
this.loader.onDidDismiss(() => {
console.log('Dismiss');
})
});
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);
}
})
}
}
}
Here's how I've solved the same issue in my project. I use this service in the HTTP Interceptor to show the loader for all the REST API calls inside my app.
loading.service.ts
import {Injectable} from '@angular/core';
import {LoadingController} from '@ionic/angular';
@Injectable({
providedIn: 'root'
})
export class LoadingService {
constructor(public loadingController: LoadingController) {
}
async present(options: object) {
// Dismiss all pending loaders before creating the new one
await this.dismiss();
await this.loadingController
.create(options)
.then(res => {
res.present();
});
}
/**
* Dismiss all the pending loaders, if any
*/
async dismiss() {
while (await this.loadingController.getTop() !== undefined) {
await this.loadingController.dismiss();
}
}
}
In the original question context this could be used like below:
...
import {LoadingService} from '/path/to/loading.service';
...
customer: any;
constructor(public loadingService: LoadingService, private customerService: CustomerService)
ngOnInit() {
this.loadingService.present({
message: 'wait. . .',
duration: 5000
});
this.customerService.getCustomer('1')
.subscribe(customer => {
this.customer = customer;
this.loadingService.dismiss();
}
}
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();
}
}
来源:https://stackoverflow.com/questions/52574448/ionic-4-loading-controller-dismiss-is-called-before-present-which-will-ke