0

I'm displaying a LoadingController when the user tries to login. Meanwhile, an API is being called.

I’m able to dismiss the LoadingController when I get a SUCCESS response from subscribe, but when I get an ERROR response, I’m not able to dismiss. Please help!

I’m a professional Python developer and a total newbie to Ionic, just started a day ago. So, please assist as such.

import { Component, OnInit } from '@angular/core';
import { ToastController, LoadingController } from '@ionic/angular';

import { CallapiService } from '../callapi.service';

@Component({
  selector: 'app-login',
  templateUrl: './login.page.html',
  styleUrls: ['./login.page.scss'],
})
export class LoginPage implements OnInit {

  userEmail = '';
  userPassword = '';
  loginUrl = 'login/';
  loginMethod = 'POST';
  postBody = {};


  constructor(
    public toastController: ToastController,
    public loadingController: LoadingController,
    private callApiService: CallapiService,
  ) { }

  ngOnInit() {
  }

  async presentToast(displayMessage) {
    const toast = await this.toastController.create({
      message: displayMessage,
      duration: 2000,
      position: 'middle',
    });
    return await toast.present();
  }

  async presentLoading(loadingMessage) {
    const loading = await this.loadingController.create({
      message: loadingMessage,
    });
    return await loading.present();
  }


  loginUser() {
    if (this.userEmail === '' || this.userPassword === '') {
      this.presentToast('Email and password are required.');
    }

    else {
      this.presentLoading('Processing...');
      this.postBody = {
        email: this.userEmail,
        password: this.userPassword,
      };
      this.callApiService.callApi(this.loginUrl, this.postBody, this.loginMethod).subscribe(
        (success) => {
          console.log(success);
          this.loadingController.dismiss();
        },
        (error) => {
          console.log(error);
          this.loadingController.dismiss();
        }
      );
      this.loadingController.dismiss();
    }

  }

}
  • Try to debug & make sure that your control goes to an error block on error. It may happen that your server is sending 200 with a custom error message. Or you may *dismiss* your loading in `complete` callback by simply appending `() => { ... }` after an error block. – Tushar Walzade Mar 08 '19 at 08:32
  • I don't think you need the last `this.loadingController.dismiss()`. The loading controller may be getting dismissed before your API has even returned. – Chris Mar 08 '19 at 09:06

4 Answers4

3

Without any service,

Same issue I faced while using Ionic 4 loading controller. After trial and error I got working solution.

As loading controller functions are using async and await because both are asynchronous functions.

dismiss() function will called before present() function because, dismiss function will not wait until creating and presenting the loader, it will fire before present() as soon function will call.

Below is working code,

   loading:HTMLIonLoadingElement;
   constructor(public loadingController: LoadingController){}

   presentLoading() {
     if (this.loading) {
       this.loading.dismiss();
     }
     return new Promise((resolve)=>{
       resolve(this.loadingController.create({
        message: 'Please wait...'
      }));
     })
   }

  async dismissLoading(): Promise<void> {
    if (this.loading) {
      this.loading.dismiss();
    }
  }

  someFunction(){
    this.presentLoading().then((loadRes:any)=>{
      this.loading = loadRes
      this.loading.present()

      someTask(api call).then((res:any)=>{
        this.dismissLoading();
      })
    })
  }
1
this.callApiService.callApi(this.loginUrl, this.postBody, this.loginMethod)
  .subscribe(
    (data) => {
      // Called when success  
    },
    (error) => {
      // Called when error
    },
    () => {
      // Called when operation is complete (both success and error)
      this.loadingController.dismiss();
    });

Source: https://stackoverflow.com/a/54115530/5442966

Junior Gantin
  • 2,102
  • 1
  • 18
  • 24
0

Use Angular property binding. Create a component to your loading:

import { Component, Input } from '@angular/core';
import { LoadingController } from '@ionic/angular';

@Component({
  selector: 'app-loading',
  template: ''
})
export class LoadingComponent {
  private loadingSpinner: HTMLIonLoadingElement;

  @Input()
  set show(show: boolean) {
    if (show) {
      this.loadingController.create().then(loadingElem => {
        this.loadingSpinner = loadingElem;
        this.loadingSpinner.present();
      });
    } else {
      if (this.loadingSpinner) {
        this.loadingSpinner.dismiss();
      }
    }
  }

  constructor(private loadingController: LoadingController) {}
}

...then in 'login.page.html' use your componente:

...    
<app-loading [show]="showLoading"></app-loading>

... in 'LoginPage' create a property 'showLoading' and set it to true or false where you whant:

//.... some source code
export class LoginPage implements OnInit {
  showLoading;
  userEmail = '';
  userPassword = '';
  loginUrl = 'login/';
  loginMethod = 'POST';
  postBody = {};

  //.... some source code

  loginUser() {
    if (this.userEmail === '' || this.userPassword === '') {
      this.presentToast('Email and password are required.');
    } else {
      this.showLoading = true;
      this.postBody = {
        email: this.userEmail,
        password: this.userPassword
      };
      this.callApiService
        .callApi(this.loginUrl, this.postBody, this.loginMethod)
        .subscribe(
          success => {
            console.log(success);
            this.showLoading = false;
          },
          error => {
            console.log(error);
            this.showLoading = false;
          }
        );
      this.showLoading = false;
    }
  }
}

This works for me, I reuse the loading component on others pages!

Recommended reading: https://angular.io/start

0

I actually ran into this exact issue and for me the answer was just to use await.

The functions for both creating and dismissing loaders return promises. What I realized was happening is that the subscribe/promise rejection was halting all other promises from completing. Now, I just await both presenting and dismissing and I have no issue:

async getData() {  
  //await presenting
  await this.presentLoading('Loading...');

  try {
    let response = await this.httpService.getData();
    await this.loadingController.dismiss();

    //...
  catch(err) {
    this.loadingController.dismiss();
    //handle error
    //...
  }
}

async presentLoading(msg: string) {
  const loading = await this.loadingController.create({
    spinner: 'crescent',
    message: msg
  });
  await loading.present();
}

I hope this simple solution helps!

GoForth
  • 573
  • 7
  • 10