Ionic2: Can't call loading.dismiss twice


#1

Hey there,

I’m using the Loading component in my LoginPage. I want a loading spinner to appear while the login request is processed.

In case the login wasn’t successful on the first try, the app crashes as soon as the loading is tried to be closed.

Error:

Error: Uncaught (in promise): TypeError: undefined is not an object (evaluating ‘leavingView.pageRef().nativeElement’) — zone.js:538

Here is my code:

import {Component} from '@angular/core';
import {Alert, Loading, NavController} from 'ionic-angular';
import {LoginService} from '../../providers/login-service/login-service';

@Component({
	templateUrl: 'build/pages/login/login.html',
	providers: [LoginService]
})
export class LoginPage {

	constructor(private nav:NavController, private loginService:LoginService) {
		
	}


	login() {
		var loading = Loading.create({
			content: 'Please wait...'
		});

		this.nav.present(loading);

		setTimeout(() => {
			loading.dismiss();
		}, 1500);

		this.loginService.load('https://randomuser.me/api/?results=10')
			.then(data => {
				loading.dismiss(); // HERE
				console.log(data);
			});
	}

}

(LoginService is a slightly changed auto generated provider)

The strange thing is: If I remove the loading.dissmiss(); there is no problem. And if I close It via setTimeout() there is no problem too. It only appears in this constellation I think.

Does anyone have an idea what the problem could be?

I don’t have any idea how to debug this issue any further.


#2

I found a strange work around. I’ve simply wrapped loading.dismiss(); into a function and called this function and the error disappears.

login () {
    
    //...

    let dismiss = function () {
        loading.dismiss(); 
    }; 

    //... 

    .then (data => {
        dimiss();
    });

    //...
}

#3

I think you just hide it, promise are known to hide when call inside then is failing.
.then (data => {
dimiss();
}).catch(e=>console.error(e));

You will see it showing up again.


#4

You are not supposed to double dismiss loading indicators. In fact, once you’ve dismissed them, you should not try to do anything with them. They don’t exist any more.


#5

@rapropos

I would like to know what to do in my scenario.

For example I am taking in 3 inputs : UniqueID, Mobile Number and Email. I call a web-service which returns if any of these values are already present in my DB. So the user can enter these field multiple times, and each time I verify them using Web Service, I call up the Loader, and then Dismiss the loader after I get the response.
So I am facing the same trouble. So in this scenario, what do you suggest? how should I go about showing a loader multiple times?


#6

Two options: (a) show it once, wait on all the events to finish, then dismiss it; or (b) create a different loader for each event. Tough for me to suggest which you think would make for a better UX.


#7

@rapropos
But then, everytime I hit the web, and recieve an error, I have to stop the loader and throw an error
alert. then when the user re enters any fields, I have to again Hit the Web service to verify and start the loader again, and dismiss it when I get the response for this web hit. And I can never be sure as to how many times the loader might need to run. It depends totally on after how many tries does the user get the mobile number unique.


#8

I don’t know what else to tell you. As I said before, you can maintain a single loading indicator across multiple events, or you can pop separate ones for each event. You can’t reuse them, and you can’t double-dispose them. That’s simply a fact of life.


#9

Seems a strange and unnecessarily complicated way to handle loading.

import { Loading, LoadingController } from 'ionic-angular';

export class LoadingPage {
 public loading: Loading;
   constructor(public loadingCtrl: LoadingController) 
 }

presentLoading() {
  this.loading = this.loadingCtrl.create({
  content: 'loading content...'
  });
  return this.loading.present()
}

someFunction() {
 this.presentLoading()
.then(() => do stuff)
.then(() => other stuff)
.then(() => this.loading.dismiss())
.catch(() => this.loading.dismiss())
}

Seems the most effective, simple approach while isolating the creation of loading in one function. It has never failed me. From what I’ve read on this post its essentially @rapropos suggestion manifested.

No reason to over complicate a simple task


#10

Not really. If you put a loading indicator in a class property, you are asking for trouble. It becomes unclear who is responsible for it. If you always make them lexically-scoped, then you immunize yourself from reuse and double-dispose bugs.


#11

@rapropos,
Is that what you’re pointing out?
Saying it’s better to use a let loading = this.loadingCtrl.create({}) in each function that calls for it?


#12

Exactly. It’s impossible to reuse a lexically scoped variable, because nobody else can touch it.


#13

I’ve reformatted my code to create a new loading instance per need. Its proven to be more manageable in spite of the extra code.


#14

Awesome Answer. Found the right way to use Loaders!