Loading overlay not showing immediately


#1

Hello,

I am having a hard time understanding why a loading overlay isn’t showing immediately.

The overlay is being created and presented at the beginning of the function that switches to a different view. The function is called when a div is tapped:

View

<div (tap)="openPage()">
    (...)
</div>

Component

openPage(): void {
    let loading: Loading = this.loadingCtrl.create("Open new view...");
    loading.present();
    (...)
    this.navCtrl.push(NewPage).then(
        () => {
            loading.dismiss();
            console.log("New page opened.");
        }
    );
}

After I tap the current view does nothing for a couple of seconds, then opens the new view and presents the loading overlay very briefly. I would like the loading overlay to be there all the time while the openPage() function is executing.

Can any of you please explain what I’m missing here? I’m using Ionic 2.2.1.

Thank you!

AP


#2

I think you’d be better off showing the loading controller in the constructor of your new page. The new page should show instantly, even if the content of the page takes a while to load.


#3

Hello rlouie, thank you.

I tried it and it still has the same problem: the loading overlay only shows up when the new view appears, after a couple of seconds. My new code is:

Current page component

openPage(): void {
	this.navCtrl.push(NewPage);
}

New page component

private loading: Loading;

constructor(private loadingCtrl: LoadingController) {
	this.loading = this.loadingCtrl.create();
	this.loading.present();
	(... slow stuff goes here...)
}


ionViewDidLoad(): void {
	this.loading.dismiss();
}

This is driving me nuts!

Thank you,

AP


#4

You’re dismissing it in the wrong place, you should dismiss after whatever slow stuff is completes. Also, no slow stuff should be in the constructor, move those out into a lifecycle event.


#5

Thank you again rlouie.

Changes made, no effect :frowning:.

  1. Kept the creation of the loading overlay in the new page’s constructor.
  2. Moved the beefy code to the new page ionViewDidLoad() lifecycle function.
  3. Issued the loading overlay dismiss() after the beefy code.

The new page’s code looks like this:

constructor(private navParams: NavParams, private loadingCtrl: LoadingController) {
	this.thing = navParams.get("thing");
	this.loading = this.loadingCtrl.create();
	this.loading.present();
}

ionViewDidLoad(): void {
	(... beefy code goes here...)
	this.loading.dismiss();
}

This means that between the tap event on the original view and the present() of the loading overlay there’s very little code.

My only explanation for the delay between the tap and the new page shows up is that it’s happening inside Angular/Ionic while processing the tap event and calling the handler function.

Original page view

<ion-card *ngFor="let thing of things">
	<div class="details" tappable (tap)="openThing(thing)">
		<ion-card-header class="name">{{thing.name}}</ion-card-header>
		<ion-card-content [hidden]="thing.items.length == 0">
			(... some content in here...)
		</ion-card-content>
	</div>
</ion-card>

Original page component

openThing(thing: Thing): void {
	this.navCtrl.push(NewPage, {thing: thing});
}

The thing object can be quite big and it’s the one that takes time loading in the new page. Is it possible that Angular/Ionic take time processing the event due to its size?

Thank you again,

AP


#6

Can you elaborate on what (beefy code) is? Is it asynchronously loading data? If so you can’t just hide the loader on the next line, you have to hide it when the async call completes. I guess I can’t imagine what else it would be…if you’re just getting already loaded data and it’s still super slow then it could be render time if it’s a huge loop. That just seems less likely.


#7

Sure.

The thing is a list of items with a couple of string attributes and an array of other objects. Something like:

{
    name: "Thing 1",
    type: "Type A",
    items: [ ... many items here ...]
}

An item is something like:

{
    name: "Item 1",
    dateTime: "2017-03-20 19:34",
    tags: [... a few strings here...]
}

An object that takes those 2/3 seconds to load has around 50 items. So, it’s not a huge structure.

The “beefy” code simply groups the items by tag, and there are around 10 different tags. The result is something like:

{
    "Tag 1": [item1 item2 ...],
    "Tag 2": [item3 item4 ...],
    ...
}

Unfortunately I have no idea how to profile Angular applications, I think I’ll have to invest time on that. I suspect that the 2/3 seconds delay is happening before the push to the new page even happens.

Thank you for helping with this.

AP


#8

My two cents: I never store loading components (or toasts or modals or other such) as object properties. It makes you prone to ownership bugs that programmers working in garbage-collected languages by and large don’t have to worry about.

The same function should always be responsible for creating, popping, and dismissing loading components. If that means you have to refactor your code, so be it. Always make them lexically-scoped variables.

let loading = this._loadings.create();
loading.present();
doBeefyThing().then(() => {
  loading.dismiss();
});

#9

Hmm, usually that delay will happen before the push if you have stuff in the constructor, because it has to run that when it builds the page. That’s why I had you move it out of the constructor.

Why don’t you set a breakpoint at the start of the “beefy” code and see how long it takes to hit that? If it hits it instantly it’s the beefy code, if it takes 2/3 then it’s before that. That’s easier than profiling, but you could definitely still profile it just using the chrome profiler.


#10

rapropos, rlouie, thank you.

I just got more confused. I did some (logging-based) profiling and the app is spending 1.5 seconds between the end of the new page constructor and the calling of the ionViewDidLoad()!

Does any of you understand what might be happening? My beefy code isn’t beefy after all, it only takes a few milliseconds!

AP


#11

I found a way to mitigate the problem from the LoadingController perspective. I still need to work on why it takes 1.5 seconds between running the constructor and ionViewDidLoad().

Ionic somehow prioritises the presentation of the layers and it’s not guaranteed that the loading overlay is displayed after the present() function is called. In fact, it returns a promise and, although the documentation doesn’t mention it, I checked the code and the promise only returns when the overlay is shown.

So, following rapropos advice and knowing that it was the new page loading that was the beefy thing here, I arranged things the following way:

	let loading:Loading = this.loadingCtrl.create({content: `Opening page...`});
	loading.present().then(() => {
		this.navCtrl.push(page, {thing: thing}).then(() => {
			loading.dismiss();
			this.logger.debug(`Page opened.`);
		});
	});

This shows the loading spinner immediately after the tap, as I originally wanted, and only dismisses it after the new page fully loads.

Now I need to work on those 1.5 seconds…

Thank you again for your help, it was crucial to get me on track!

AP