Increase Ionic Scroll Performance with Virtual Scroll & Infinite Scroll Last update: 2018-02-13

Increase Ionic Scroll Performance with Virtual Scroll & Infinite Scroll

When working with Ionic we mostly use the classic Ionic list and iteration patterns we know about. However, if you have a lot of data to display it makes sense to keep an eye on the Ionic Scroll Performance of your application.

To improve the performance of your Ionic lists there are 2 patterns we can use:

As it’s not immediately clear when to use which of these and what they actually add to your app let’s take a closer look at both of them.

Setup a New Ionic Scrolling Dummy App

Before we dive into the 2 patterns we need a super minimalistic setup for a dummy app so we can receive some JSON data. As always you can start a new Ioni capp like this:

ionic start devdacticScroll blank

To make HTTP calls later we also need to import the apropriate module so change your app/app.module.ts to:

import { BrowserModule } from '@angular/platform-browser';
import { ErrorHandler, NgModule } from '@angular/core';
import { IonicApp, IonicErrorHandler, IonicModule } from 'ionic-angular';
import { SplashScreen } from '@ionic-native/splash-screen';
import { StatusBar } from '@ionic-native/status-bar';

import { MyApp } from './app.component';
import { HomePage } from '../pages/home/home';

import { HttpClientModule } from '@angular/common/http';

@NgModule({
  declarations: [
    MyApp,
    HomePage
  ],
  imports: [
    BrowserModule,
    IonicModule.forRoot(MyApp),
    HttpClientModule
  ],
  bootstrap: [IonicApp],
  entryComponents: [
    MyApp,
    HomePage
  ],
  providers: [
    StatusBar,
    SplashScreen,
    {provide: ErrorHandler, useClass: IonicErrorHandler}
  ]
})
export class AppModule {}

Now we are ready so let’s start with the first list pattern.

Implementing Ionic Infinite Scroll

Infinite scroll describes a list which can be scrolled down (or up) all the time while adding new items. Whenever we reach the end of our current loaded list, a function will be triggered and new items can be added to the list.

This means, the infinite scroll is great for pagination.

If your api returns a number of items plus a pages count you can always ask for more results on another page. A typical response might look like this:

{
  "page": 0,
  "maxPages" 18,
  "results": [
   // Actual result objects
  ]
}

In those situations the page parameter needs to be passed to the API, and we will build a little example with exactly that behaviour.

Inside the view of our app we need an iteration over tan array of items. Nothing new so far, but below the list we add the ion-infinite-scroll component. This component will automatically detect when the user scrolls a specified distance from the bottom of the page and trigger the (ionInfinite) function you can declare.

Additional you can also change the loading text, animation and also the position (top or bottom) of the infinite loading component.

Let’s get started by changing your pages/home/home.html to:

<ion-header>
  <ion-navbar>
    <ion-title>
      Infinite Scroll
    </ion-title>
  </ion-navbar>
</ion-header>

<ion-content>

  <ion-card *ngFor="let user of users">
    <ion-item>
      <ion-avatar item-start>
        <img [src]="user.picture.medium">
      </ion-avatar>
      <h2 text-capitalize>{{ user.name?.first }} {{ user.name?.last }}</h2>
    </ion-item>
    <ion-card-content>
      {{ user.email }}
    </ion-card-content>
  </ion-card>

  <ion-infinite-scroll (ionInfinite)="loadMore($event)" loadingSpinner="bubbles" loadingText="Loading Users...">
    <ion-infinite-scroll-content></ion-infinite-scroll-content>
  </ion-infinite-scroll>

</ion-content>

Now we just need to make the according calls inside our controller to get some data and fill our users array. We make use of the Randomuser API which actually also happens to allow pagination (yeaaah)!

The only thing you need to take care of is not overriding your users all the time but adding the new array of results to the old entries to keep your existing data.

Also if the action was triggered by the infinite scroll we need to finish it with infiniteScroll.complete() to hide the loading and set it back to the regular state.

Of course your pages logic may vary, but this would be an example how to increase your page of the results with each infinite loading call.

Open your pages/home/home.ts and change it to:

import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';
import { HttpClient } from '@angular/common/http';
import "rxjs/add/operator/map";

@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {
  users = [];
  page = 0;
  maximumPages = 3;

  constructor(public navCtrl: NavController, private httpClient: HttpClient) {
    this.loadUsers();
  }

  loadUsers(infiniteScroll?) {
    this.httpClient.get(`https://randomuser.me/api/?results=20&page=${this.page}`)
    .subscribe(res => {
      this.users = this.users.concat(res['results']);
      if (infiniteScroll) {
        infiniteScroll.target.complete();
      }
    })
  }

  loadMore(infiniteScroll) {
    this.page++;
    this.loadUsers(infiniteScroll);

    if (this.page === this.maximumPages) {
      infiniteScroll.target.disabled = true;
    }
  }

}

For the example we also added a maximum number of pages because most API results will eventually end at some point. Once that happens we can directly disable the infinite scroll behaviour by calling infiniteScroll.enable(false).

The resulting app will look like below!

ionic-infinite-scroll

When should I use Ionic Infinite Scroll?

If your API offers pagination for your results. You can make calls and receive a small bunch of results which helps to display data faster to the user. Whenever the user needs more data the call will be placed and again the result will be available faster as the response is smaller than if it would contain all results!

So if you can split up your API results and trigger the load of additional data, the infinite scroll is exactly what you need.

Implementing Ionic Virtual Scroll

The Ionic Virtual Scrolling list offers a special behaviour which you might know from native iOS development: The list takes a complete array of data but not all items are rendered immediately, only the ones the user can see and a few nearby. When the user starts scrolling, views will be reused and filled with information of the new rows.

For this pattern let’s start with the logic of our page. We don’t need the pages logic from before, but this time we should make an API call to get a massive result (you could also change 100 to 1000 here). So go ahead and change your pages/home/home.ts one more time to:

import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';
import { HttpClient } from '@angular/common/http';
import "rxjs/add/operator/map";

@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {
  users = [];

  constructor(public navCtrl: NavController, private httpClient: HttpClient) {
    this.loadUsers();
  }

  loadUsers() {
    this.httpClient.get(`https://randomuser.me/api/?results=100`)
    .subscribe(res => {
      this.users = res['results'];
    })
  }

}

As you can see, we get ”all” results of the API. Actually 100 are not all here, but imagine this would be everything.

This means, we expect a huge response with potentially 100 rows of data. And this can be a lot more in the enterprise context as well!

If you would wrap all of those results into a simple ngFor list and call it a day you will regret it very soon as the performance will give you hell. All cells and views of the list will be created and rendered, but you actually only need the few that the user can see!

Therefore, we can wrap our results into the Virtual Scroll like this inside your pages/home/home.html:

<ion-header>
  <ion-navbar>
    <ion-title>
      Virtual Scroll
    </ion-title>
  </ion-navbar>
</ion-header>

<ion-content>

  <ion-list [virtualScroll]="users" [approxItemHeight]="'88px'">
    <ion-card *virtualItem="let user">

      <ion-item text-capitalize>
        <ion-avatar item-start>
          <img [src]="user.picture?.thumbnail">
        </ion-avatar>
        <h2>{{ user.name?.first }} {{ user.name?.last }}</h2>
      </ion-item>

      <ion-card-content>
        {{ user.email }}
      </ion-card-content>

    </ion-card>
  </ion-list>

</ion-content>

You might need to use it a few times until you know the syntax by heart, but once you got it this is a powerful tool you need to know about.

Also, it is recommended to no use the standard image tage inside the elements but the ion-img component.

However when developing this tutorial I could get it to work with the special Ionic component, and there are countless open issues (and sometimes closed for no reason) on Github showing that this component is not really working as expected.

Perhaps give the ion-img a try but be aware that it might have some bugs. But this counts only for the image, not the virtual scroll itself!

When should I use Ionic Virtual Scroll?

If you API delivers huge amounts of data (big data) and you want to display all the data for the user. A regular list would have a poor performance, therefore use the virtual scroll if you have the full response at hand.

Conclusion

The 2 special list patterns can be a real game changer if your app suffers from poor performance. And as we’ve seen, the implementation is so fast that you can’t argue you don’t know how it works anymore!

You can also find a video version of this article below.