Page taking too long to load

Hello,

I have a page that displays around 120 items as an ion-list. The app (Ionic 2.3.0) navigates to this page from the home page with a NavController.push().

Each of the list items is an instance of a simple custom component, that displays the item’s name, type, etc… I implemented it as a component because it can be re-used in other circumstances.

On an Android Moto G3 the page is taking more than 4 seconds to load. I added some tracing logs and the app is spending time on the following:

  • 300ms creating and displaying the Loading overlay

  • 1500ms somewhere between the end of the page constructor and the start of ionViewDidLoad()

  • 1000ms between the end of ionViewDidLoad() and the start of ionViewWillEnter()

  • 1500ms between the end of ionViewWillEnter() and ionViewDidEnter()

I don’t know where I can start to debug what’s happening between the calls to those methods. Any ideas? What options do I have to reduce this delay?

Can I pre-load, cache the page and re-use it, instead of creating a new instance on every push()?

Thank you for reading this, cheers,

AP

Can you put together a minimal example to look at?
We can help you out and figure out how to get things in a better place, but we need code for this.
A simple github repo with a sample project should do.

Hello, thank you for getting back to me. The code is actually quite simple:

*** home.ts ***

let loading:Loading = this.loadingCtrl.create({content: "Opening new page..."});
loading.present().then(() => {
	this.navCtrl.push(NewPage, {items: itemList}).then**(() => {
		loading.dismiss();
	});
});

The setItems() function bellow groups the supplied items by type. From my logs I can see it takes a few milliseconds to run.

*** new-page.ts ***

constructor((...)) {
	this.items = {}
}

ionViewDidLoad(): void {
	this.setItems();
}

The new page HTML:

*** new-page.html ***

<ion-content>
	<div *ngFor="let type of getItemTypes()">
		<custom-item *ngFor="let item of items[type]" [item]="item"></custom-item>
	</div>

	<div *ngFor="let item of thing.disabledItems">
		<custom-item [item]="item"></stack-item>
	</div>
</ion-content>

Finally, the custom component (the TypeScript file only holds the item variable). As there are around 120 items, it has to create 120 of these. Is that a problem?

*** custom-item.component.html ***
<ion-card>
	<div class="rank">
		<strong *ngIf="rank" ion-text color="secondary">{{rank}}</strong>
		<img src="{{'assets/images/'+getCostIcon()+'.png'}}">
	</div>

	<ion-card-header>
		<div class="name">
			(...)
		</div>
		<button ion-button icon-only clear item-right (click)="edit()">
			<ion-icon small name="settings"></ion-icon>
		</button>
	</ion-card-header>

	<ion-card-content>
		<div class="types-list">
			<ion-badge color="secondary" *ngFor="let type of item.types">{{type}}</ion-badge>
		</div>
	</ion-card-content>
</ion-card>

Thank you again, cheers,

AP

mind throwing this into a simple git repo to look at? There’s some obvious place here where you can speed things up, but I’d like to see the full context.

Hello thank you again!

For the time being this is a closed source app, but I’ll segregate the relevant code and push it to Github. Just give me half an hour!

AP

Hello again,

Here’s a small project that illustrates the delay: https://goo.gl/aO28Z3

Although the list also has around 120 items, it runs slightly faster on my Android phone: it takes 2 seconds instead of 4. But the items have less attributes than in my project (I would say they are half the size) and the custom component is missing some functions.

Thank you, cheers,

AP

Did you try it without this getCostIcon() operation? Seems like an expensive one to me when doing something like this.

And does this actually work?

@Input()
private item: any;

Okay I’ve cloned your repo and see the problem occur as well. We can exclude the input and getCostIcon() from the issue I guess. Those don’t seem to be the culprit.

Okay, couple of things. I don’t think you need the loader, toss it. Don’t pass 120 items through the navparams, but do it through a shared service (which both pages can access). Don’t set the items on ionViewDidLoad(); but already call it in your constructor.

Your slow-page.ts could look like this:


import {Component} from "@angular/core";
import {NavController, NavParams} from "ionic-angular";
import { Dataprovider } from '../../providers/dataprovider';
@Component({
	selector: 'page-slow-page',
	templateUrl: 'slow-page.html',
})
export class SlowPage {

	items: any = {};

	constructor(public navCtrl: NavController, public navParams: NavParams, public dataprovider: Dataprovider) {
		this.setItems();
	}

	ionViewDidLoad(): void {

	}

	private setItems(): void {
		for (let item of this.dataprovider.items) {
			for (let tag of item.tags) {
				if (!this.items[tag]) {
					this.items[tag] = [];
				}
				if (this.items[tag].indexOf(item) == -1) {
						this.items[tag].push(item);
				}
			}
		}
	}

	getTags(): Array<string> {
		return Object.keys(this.items).sort();
	}

Your Home.ts like this:

import {Component} from "@angular/core";
import {Http} from "@angular/http";
import {NavController, LoadingController, Loading} from "ionic-angular";
import {Dataprovider} from '../../providers/dataprovider';

import {SlowPage} from "../slow-page/slow-page";

@Component({
	selector: "page-home",
	templateUrl: "home.html"
})
export class HomePage {

	items: Array<any>;

	constructor(public http: Http, public navCtrl: NavController,
		public loadingCtrl: LoadingController, public dataprovider: Dataprovider) {

		http.get("assets/items.json").subscribe((result) => {
			this.items = result.json();
			this.dataprovider.items = this.items;
		});
	}

	openSlowPage(): void {
			this.navCtrl.push(SlowPage);
	}

}
1 Like

I think I would refactor some things anyhow, but this is just to illustrate how you could solve you slow loading page problem :slight_smile: Another freebie: don’t use any unless you really need to.

Check out the result in this screencast: https://youtu.be/Xfs2jTf-TSI

Hello luukschoen!

Thank you so much for taking the time to look at this. I will try this tomorrow morning and will report back!

Cheers,

AP

Hello,

Unfortunately, the changes didn’t improve things significantly. The loading of the page now takes around 3 seconds, mostly due to the removal of the LoadingController overlay (although without it the UX is terrible). The replacement of the NavParam with a service didn’t improve anything at all.

@mhartington, you mentioned that there were some obvious things to improve. Could you please send me in the right direction?

Thank you again for helping with this, cheers,

AP

I just had a quick look at your repo without trying it but a wild guess is that you should maybe try to use promises to iterate your list and tags?

In following page, on ionViewDidLoad you gonna iterate on all your list and all your pages without any promise, therefore, if I’m not wrong, the app gonna wait for the end of that process before doing something else…

try something like (rough code, there are probably better solution):

ionViewDidLoad(): void {
	this.setItems().then(() => {
               // Now it's done
             });
}

private setItems(): Promise<{}> {
    return new Promise((resolve) => {
	for (let item of this.navParams.data.items) {
		for (let tag of item.tags) {
			if (!this.items[tag]) {
				this.items[tag] = [];
			}
			if (this.items[tag].indexOf(item) == -1) {
					this.items[tag].push(item);
			}
		}
	}

          resolve();
       });
}

Thank you reedrichards.

That function only takes a few milliseconds to run. The vast majority of the time is spent between the calls to the constructor and ionViewDidLoad() (1500ms), ionViewWillEnter() (1000ms) and ionViewDidEnter() (1500ms). Those functions themselves don’t take any time at all to run.

So, whatever is taking those 4 seconds is happening in Ionic, between the calls.

oki doki.

I just cloned your repo, added the android platform and build it to test it on my samsung edge and I’ve to say it wasn’t slow. I accessed the “slow-page” in maybe 1 sec something like that, but for sure not 4 sec

Your system information:

Cordova CLI: 6.5.0
Ionic Framework Version: 3.0.1
Ionic CLI Version: 2.2.1
Ionic App Lib Version: 2.2.0
Ionic App Scripts Version: 1.3.0
ios-deploy version: 1.9.0
ios-sim version: 5.0.13
OS: macOS Sierra
Node Version: v7.2.1
Xcode version: Xcode 8.3 Build version 8E162

android 6.1.2

The 4 seconds is in the full app. That example I pushed to Github takes around 2 seconds. Your Edge is beefier than my Moto G3, that explains why!

Thank you!

Oki doki, I tried :wink: Good luck

Same here. Whenever I test (iPhone 5c or browser) it isn’t slow (anymore). I definitely saw less jerkiness with my alterations. I wouldn’t pass 120 items through my navparams, even though @APInto might think it doesn’t make a difference, I think it’s bad design to not share so much data through a service :wink: I think you might doing a little bit much of heavy lifting inside the view, which could be done way before rendering it into te view. Good luck :slight_smile:

Thank you guys!

You have a point @luukschoen. I am a backend developer and typically I tend to keep it DRY. I do understand, though, that in UX we need to be redundant to prevent lag. For example, I am calculating those cost icons at display time and they can be calculated every time the item cost is updated.

Just a question: apart from best practices aspects, what’s the problem of passing data though the NavParams? Is it processed somehow by Ionic/Angular or is it just a simple reference pass?

Thanks.
Using below code solves my problem
let loading:Loading = this.loadingCtrl.create({content: “Loading…”});
loading.present().then(() => {
this.navCtrl.push(NewPage, {items: itemList}).then**(() => {
loading.dismiss();
});
});

1 Like