38

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();
}
rami bin tahin
  • 1,897
  • 2
  • 10
  • 16

23 Answers23

123

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();
    }
  );
rami bin tahin
  • 1,897
  • 2
  • 10
  • 16
  • Works perfectly. I just had to edit this line (then((a)) . to avoid compile error. }).then((a) => { . – Squapl Recipes Nov 18 '18 at 06:57
  • 1
    thats great but note that dismiss() will be called only if getCustomer succeeded. you need to call it again if there was an error. example: ..getCustomer.subscribe(customer => {... dismiss(); }, error => {... dismiss(); }). and maybe show an error message using alertController – rami bin tahin Nov 18 '18 at 08:54
  • 1
    Didn't find anything better for now – Jeremy Belolo Jan 26 '19 at 22:38
  • What happened to the "content" we had before? I want to put my own spinner on the content but there is no content anymore. – Francisco Souza Feb 23 '19 at 11:51
  • a Suggestion @ramibintahin if we are using this loader at multiple times at once we have to pass a unique identifier to avoid conflicts – Ravi Mehta Mar 08 '19 at 12:23
  • Thanks! I also added an optional message parameter to present(), which may come in handy sometimes. – O.S.Kaya Mar 13 '19 at 13:31
  • @O.S.Kaya I dont like to show message on loader but I always use optional header and message parameters with alertController and call it from a service = less code to write :) – rami bin tahin Mar 14 '19 at 14:34
  • While this can work, this can cause problems with orphaned loadings. My two cents in my answer (above) :). I usually only want 1 instance of a loading. If you have multiple instances, it can be messy, in my opinion. – Martin Apr 27 '19 at 22:06
  • 6
    I like this solution which works quite well. I have only experienced this console error from time to time: "overlay does not exist". I solved this checking if getTop() return some values loadingController.getTop().then(value => value ? loadingController.dismiss() : null); – A. D'Alfonso Aug 01 '19 at 08:31
  • ramib in tahin, thanks for your solution I thought about the issue that you have to call the dismiss both in the success handler and in the error handler of your subscription. Maybe it's an solution to use the rxjs finalizer? See code as example: this.customerService.getCustomer('1') .subscribe( customer => { this.customer = customer; }, error => { console.log(error); } ).add(() => this.loading.dismiss()); – Kjell Derous Sep 05 '19 at 07:58
  • I think it will be easier if you wrote it in finally. but in my case, I do some http requests inside of subscribe, so I write the dismiss() after I finish with all calls. – rami bin tahin Sep 05 '19 at 09:08
  • Hey @ramibintahin I had used the loading as a service and during my http requests I am passing the mod]e accordingly to open and close the modal.Now what I noticed is duration is playing the major role for dismissing the modal, sometimes what happens is due to poor connection even if my data is not loaded the loading dismiss is called. please have a look at the code this is the service file for [loading](http://collabedit.com/ftrte).Also sometimes even if the data is loaded still the loader waits for the duration to be completed then only it get dismissed. – Abhishek Feb 07 '20 at 19:36
  • hey @Abhishek, if you are using the service in my answer, just remove "duration: 5000," and it will only dismiss after calling dismiss(). I checked the link and it is not the same as my service and I can see that the logic is not correct. – rami bin tahin Feb 08 '20 at 12:43
  • Thanks it worked.But I love that you help me out what went wrong with the logic in my code ? – Abhishek Feb 08 '20 at 17:04
  • I don't know why but, in my case it shows up and dismiss every loader alert on screen and that effect is ugly. It works, but ugly effect. Would be awesome if just one loading alert is showed up while doing multiple requests and you use the loading-service in interceptor. – RRGT19 Jul 03 '20 at 00:05
  • @RRGT19 this solution works perfectly for my case. you can call dismiss() in the last request if the requests are nested, or you can group all observables and call dismiss after all the observables are complete. btw I like your idea, go ahead and try to do it. I specialized in .net backend, so I do not use ionic now. – rami bin tahin Jul 03 '20 at 00:32
9

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();
  }
}
Andrii S.
  • 236
  • 3
  • 5
7

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);
    }

  }
Code Spy
  • 9,626
  • 4
  • 66
  • 46
4

This way it also resolved concurrent API call loader dismiss issue fix. You can call those functions to form the interceptor too. There is no fix duration, because if any call needs much time loader will continue. But if anyone gives duration then if that API won't stop by this time loader will stop

import { Injectable } from '@angular/core';
import { LoadingController } from '@ionic/angular';

@Injectable({
  providedIn: 'root'
})
export class LoadingService {
  isLoading = false;
  loaderCounter = 0;
  loading: HTMLIonLoadingElement;

  constructor(public loadingController: LoadingController) {}

  async present() {
    this.loaderCounter = this.loaderCounter + 1;

    if (this.loaderCounter === 1) {
      this.isLoading = true;
      const { loadingDuration, loadingMessage = loadingDefaultOptions.loadingMessage, loadingCssClass } = options;
      this.loading = await this.loadingController.create({
        duration: loadingDuration,
        message: loadingMessage,
        cssClass: loadingCssClass
      });
      await this.loading.present();
    }
  }

  async dismiss() {
    this.loaderCounter = this.loaderCounter - 1;
    if (this.loaderCounter === 0) {
        this.isLoading = false;
        await this.loading.dismiss();
    }
  }
}
  • 1
    This works great when you use it in interceptor, also works awesome with multiple HTTP requests. Only 1 loader alert is showed up even if I did 2 requests. Nice job. – RRGT19 Jul 03 '20 at 00:04
3

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;
    }

}
Martin
  • 431
  • 1
  • 6
  • 12
3

Using a list worked better for me

import { Injectable } from '@angular/core';
import { LoadingController } from '@ionic/angular';

@Injectable({providedIn: 'root'})
export class LoadingService {
    private loaders = new Array<HTMLIonLoadingElement>();
    constructor(public loadingController: LoadingController) { }

    present(options?: object) {
        if (this.loaders.length === 0) {
            this.loadingController.create(options).then(loader => {
                this.loaders.push(loader);
                loader.present();
            });
        }
    }

    async dismiss() {
        if (this.loaders && this.loaders.length > 0) {
            this.loaders.forEach(async loader => {
                await loader.dismiss()
                    .then(() => {
                        loader = null;
                    })
                    .catch(e => console.log(e))
                    .finally(() => this.loaders = new Array<HTMLIonLoadingElement>());
            });
        }
    }
}
2

The simple way is Add setTimeOut function :

setTimeout(() => {
      this.loading.dismiss();
    }, 2000);
1

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 think this is safest way to use loader in interceptors. show loaders and hide loaders accordingily. Superb – Luckyy Mar 28 '21 at 14:38
1

I know this question is to ask about a year ago. I facing the same problem. I just want to post my solution. I hope the upcoming visitor will get help.

async dismissLoading() {
    console.log("dismiss");
    this.isLoading = false;
  }
 private async presentLoading(msg) {
    console.log("loading");
    const loading = await this.loadingCtrl.create({
      message: msg,
    });
    await loading.present();
    var timer = setInterval(() => {
      if (!this.isLoading) {
        loading.dismiss();
        clearInterval(timer);
        console.log("set dismiss");
      }
    }, 1000);
  }
  async loadingmsg() {
    this.isLoading = true;
    await this.presentLoading("Please wait while...");
  }

this solution work for me. Please correct me if I am wrong.

Anil
  • 473
  • 1
  • 5
  • 14
1

My solution for this problem, was to set a status variable. Here it is:

@Injectable()
    export class LoaderSerive {
    private status: 'pending' | 'dismissed' | 'present' = 'dismissed';

    constructor(public loadingCtrl: LoadingController) {}

    public show() {
        if (this.status === 'present') {
            this.hide();
        }

        this.status = 'pending';

        this.loadingCtrl.create({
            id: 'spoon-indicator-1',
            spinner: null,
            message: `
                <div>
                    <div class="loading-indicator--position">
                        <div class="loading-indicator">
                            <div class="bowl">
                                <div class="spoon"></div>
                                <div class="bowl-content"></div>
                            </div>
                        </div>
                    </div>
                </div>`,
            duration: 6000
        })
        .then((loader) => loader.present())
        .then(() => {
            if (this.status === 'pending') {
                this.status = 'present';
            } else {
                this.hide();
            }
        });
    }

    public hide() {
        this.loadingCtrl
            .dismiss(null, undefined, 'spoon-indicator-1')
            .catch((err) => Utilities.log('Loader error!', err))
            .then(() => this.status = 'dismissed');
    }
}
Yordan Nikolov
  • 2,598
  • 13
  • 16
0

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();
  }
ppreetikaa
  • 1,149
  • 2
  • 15
  • 22
0

This onDidDismiss() event should created after .present() function called.

Example:

this.loader.present().then(() => {
            this.loader.onDidDismiss(() => {
                console.log('Dismiss');
            })
        });
Lê Văn Hiếu
  • 171
  • 1
  • 6
0

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);
        }
      })
    }
  }
}
Martin
  • 191
  • 2
  • 5
  • Hi i tried this solution but it's not working. Are you calling the dismiss on subscription completed ? Because in my log it showing that dismiss calls before the "LoadingService presented" printed.. I'm on ionic 4, Angular 8. I call an API using httpClient.get (observable) – cumibulat Aug 23 '19 at 11:32
0

After trying everything, here's what I finally came up with. Seems to be working well so far.

Trying to make use of setInterval with a 500ms interval. I also tried to keep the function non-async so that it may be easily used in the consuming end.

import { Injectable } from '@angular/core';
import { LoadingController } from '@ionic/angular';

@Injectable({ providedIn: 'root' })
export class UiService {
    constructor(private loading: LoadingController) { }

    private loader: HTMLIonLoadingElement;
    private loaderLoading = false;

    public showLoading(message: string) {
        this.loaderLoading = true;
        this.loading.create({
            message,
            showBackdrop: true
        }).then(load => {
            this.loader = load;
            load.present().then(() => { this.loaderLoading = false; });
        });
    }

    public dismissLoading() {
        const interval = setInterval(() => {
            if (this.loader || !this.loaderLoading) {
                this.loader.dismiss().then(() => { this.loader = null; clearInterval(interval)});
            } else if (!this.loader && !this.loaderLoading) {
                clearInterval(interval);
            }
        }, 500);
    }
}
Faisal Rashid
  • 368
  • 1
  • 11
0

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();
      })
    })
  }
0

I found a new way to do this. I hope it help! It’s using the id of loading. So if you have many loadings you don’t want to dismiss wrong loading.

In the service:

async showLoading(loadingId: string, loadingMessage: string = 'Loading...') {
            const loading = await this.loadingCtrl.create({
              id: loadingId,
              message: loadingMessage,
              spinner: 'circles'
            });
            return await loading.present();
}

async dismissLoader(loadingId: string) {
  return await this.loadingCtrl.dismiss(null, null, loadingId).then(() => console.log('loading dismissed'));
}

In the component calling the loading:

await this.globalVars.showLoading('ifOfLoading')

Dismissing the loading:

this.globalVars.dismissLoader('ifOfLoading')
Raul Barros
  • 1
  • 1
  • 1
0

Ionic 5 - Simple and short, using setTimeout, setInterval is rather a workaround than a solution.

In my case, I had presentLoading method async and dismissLoading method sync.. causing this issue.

I just added async await to my dismissoading and it works fine perfectly. for safety switch of not having 'overlay don't exisit' error added a boolean only to dismiss if loader present

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

  async dismissLoading() {
    if (this.loadingPresent) {
      await this.loadingController.dismiss();
    }
    this.loadingPresent = false;
  }
minigeek
  • 2,766
  • 1
  • 25
  • 35
0

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);
  }
 
}
Judson Terrell
  • 4,204
  • 2
  • 29
  • 45
0

Another approach using RxJs:

import {Injectable} from '@angular/core';
import {LoadingController} from '@ionic/angular';
import {BehaviorSubject, of} from 'rxjs';
import {filter, pairwise, scan, switchMap} from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class LoadingService {

  private dialog;
  private toggleLoading$ = new BehaviorSubject<number>(0);

  constructor(private loadingController: LoadingController) {
    this.toggleLoading$.pipe(
      scan((acc, curr) => acc + curr, 0),
      pairwise(),
      switchMap(([prev, curr]) => {
        if (prev <= 0 && curr > 0) {
          return this.loadingController.create();
        }
        if (prev > 0 && curr <= 0) {
          this.dialog?.dismiss();
        }
        return of(null)
      }),
      filter(d => !!d)
    ).subscribe((d) => {
      d.present();
      this.dialog = d;
    });
  }

  showLoading() {
    this.toggleLoading$.next(1);
  }

  hideLoading() {
    this.toggleLoading$.next(-1);
  }

}
Álvaro Herrero
  • 151
  • 1
  • 8
0

First Option is recommendable, but i used this code for me. works for me.

 setTimeout(() => {
    this.loading.dismiss();     
 }, 1500)
TonyInuma
  • 111
  • 1
  • 3
0

Have even an easier solution that seems to work for me:

Just an an await to your present function call. To demo the issue that dismiss may be called before the loading indicator:

ionViewDidEnter() {
    console.log('A');
    this.presentLoading('Loading ...');
    console.log('E');
    this.databaseService.getAllTracks().then(data => {
      this.tracks = data;
      this.hideLoading();
    });
  }

  private async presentLoading(text) {
    console.log('B');
    this.gpsLoadingEl = await this.loadingController.create({
      message: text,
    });
    console.log('C');
    await this.gpsLoadingEl.present();
    console.log('D');
  }

  private async hideLoading() {
    await this.gpsLoadingEl.dismiss();
  }

If your data call comes back quickly, this will output: ABE and dismiss will fire before loaded causing the error.

So if you just an extra await await, you will get: ABCDE

  async ionViewDidEnter() {
    console.log('A');
    await this.presentLoading('Loading ...');
    console.log('E');
    this.databaseService.getAllTracks().then(data => {
      this.tracks = data;
      this.hideLoading();
    });
  }

  private async presentLoading(text) {
    console.log('B');
    this.gpsLoadingEl = await this.loadingController.create({
      message: text,
    });
    console.log('C');
    await this.gpsLoadingEl.present();
    console.log('D');
  }

  private async hideLoading() {
    await this.gpsLoadingEl.dismiss();
  }
Shaun
  • 11
  • 1
-1

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();
  }
}
fifth
  • 4,249
  • 9
  • 45
  • 62
  • yes, its the same as loading.present().then(...), but as I said in my question, the request for data will not go until the loading is done (so it will be slower). In my solution it will go simultaneously. – rami bin tahin Jan 30 '19 at 09:29
-1

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
    }
}