Virtual Scroll in Ionic 2

Boosting Scroll Performance in Ionic 2



·

Scrolling is one of the most common interactions with a mobile application, and it is extremely important that it feels right. Native applications have lists that scroll smoothly, they are responsive to touch and changes of directions, they accelerate and decelerate in a way that feels natural.

It’s important that we can capture this same feeling with web tech, but it can be pretty hard to do. When lists become long, the DOM (Document Object Model) becomes larger and larger and much more resource intensive to manipulate. The DOM is usually the bottleneck for a lot of performance issues. The frameworks that can handle this well feel just like a native application, and the ones that don’t stick out like a sore thumb.

Virtual Scrolling addresses this issue. In Ionic 1 this was called Collection Repeat, in Ionic 2 it is called Virtual Scroll, but it is a concept that is used widely in HTML5 mobile development. The basic idea is that we only create enough elements in the DOM to display the list data that is currently on screen, and we recycle those DOM elements to display new data as they scroll off screen. This diagram should help explain the concept:

DOM Recycling for Mobile Lists

NOTE: This is just my understanding of how the process works in general, not necessarily the exact way it is implemented in Ionic 2.

So, we have enough DOM elements to fill up the screen, plus a few more as a bit of a buffer. As elements scroll off screen they are recycled and used for new data that is about to come on screen. Without virtual scrolling it would look more like this:

List Scrolling

Now all of the DOM elements that we will ever need for the list are created right away, which causes the DOM to become very bloated and slow. The example above wouldn’t be so bad, as we only have maybe 20 items or so, but when you start extending it out to 1000’s of items it starts to become a real problem.

This is why we use frameworks like Ionic, and why I always stress that it’s a really bad idea to just try and create your own user interface – complex performance improvements are handled right out of the box.

So let’s take a look at an actual example in Ionic 2.

Before we Get Started

Before you go through this tutorial, you should have at least a basic understanding of Ionic 2 concepts. You must also already have Ionic 2 set up on your machine.

If you’re not familiar with Ionic 2 already, I’d recommend reading my Ionic 2 Beginners Guide first to get up and running and understand the basic concepts. If you want a much more detailed guide for learning Ionic 2, then take a look at Building Mobile Apps with Ionic 2.

Generate a new Ionic 2 application

We’re going to generate a new tabs application so that we can create both a standard list and a virtual scroll list in different tabs.

Run the following command:

ionic start virtual-scroll tabs --v2

Create the Data

We need some data to display in our lists, so we’re going to auto generate a big data set using a for loop.

Modify the constructors of both home.ts and about.ts to reflect the following:


  items: any;

  constructor() {

    this.items = [];

    for(let i = 0; i < 2000; i++){

      let item = {
        title: 'Title',
        body: 'body',
        avatarUrl: 'https://avatars.io/facebook/random'+i
      };

      this.items.push(item);
    }

  }

Create a Standard List

First we’re going to set up a standard list to display the data we created in the constructor.

Modify home.html to reflect the following:

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

<ion-content>

  <ion-list>

    <ion-item *ngFor="let item of items">
      <ion-avatar item-left>
        <ion-img [src]="item.avatarUrl"></ion-img>
      </ion-avatar>

      <h2>{{item.title}}</h2>

      <p>{{item.body}}</p>

    </ion-item>

  </ion-list>

</ion-content>

In this case we are just using the standard *ngFor directive to loop over all of the items in our array and create list items for them. Since we are not using virtual scroll, this means that DOM elements are going to be created for every single item. If you try to run the application now, you will likely even notice there is a considerable amount of lag just in loading the app.

Create a Virtual Scrolling List

Now we’re going to create a list that uses virtual scroll.

Modify about.html to reflect the following:

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

<ion-content>

  <ion-list [virtualScroll]="items">

    <ion-item *virtualItem="#item">
      <ion-avatar item-left>
        <ion-img [src]="item.avatarUrl"></ion-img>
      </ion-avatar>

      <h2>{{item.title}}</h2>

      <p>{{item.body}}</p>

    </ion-item>

  </ion-list>

</ion-content>

The syntax is a little different, this time we are using [virtualScroll] and *virtualItem, but the concept is the same. We are setting up some data that will be looped over for the list. The only difference now is that this list will now use virtual scrolling instead of the standard scrolling.

If you were to now remove the code for the standard list, and run the application with just the virtual scrolling list, you shouldn’t notice any lag anymore. You can scroll the list as long and as fast as you like and it will appear to behave just like a normal list, but behind the scenes all those DOM elements are being recycled to improve performance.

When using Virtual Scroll, remember to keep in mind the performance tips list in the documentation, these include:

  • Using <ion-img> instead of <img> which will prevent unnecessary HTTP requests being made to load images that are not seen. Image sizes should be kept constant.

  • It’s important for the virtual scroll to know approximately how big your items will be, since it needs to know how many items would be required to fill up the screen. You can help this process by specifying an approxItemWidth and approxItemHeight if it does not already suit the default of 40px.

  • Avoid changing the dataset used to generate the list, as the entire virtual scroll will need to be reset and this is a costly operation. virtualTrackBy should be used for a changing dataset.

Summary

For small data sets, virtual scrolling most likely won’t make too much of a difference, but if you are dealing with large amounts of data in lists then it is vitally important that you use virtual scrolling.

  • Matt

    “Avoid changing the dataset used to generate the list, as the entire virtual scroll will need to be reset and this is a costly operation”

    Does this mean you shouldn’t use this with infinite scroll?

    • I’m not sure about that Matt, I would assume it can be used with infinite scroll (since you’re adding to the dataset, not changing it completely) but that would be a question for the Ionic devs.

    • we

      Yes True

    • At least with beta4 I would recommend not using infinite scroll with virtual scroll. However, we’d like to add more methods to virtual scroll to make it easier to add/remove data to it without reseting the entire list. This would include maintaining scroll position. There’s more optimizations we can do to virtual scroll to improve performance further, stay tuned!

      • itlr

        Hi Adam, “This would include maintaining scroll position.” – ionic2 now is at beta 7, has this been included?

  • Bahgat Mashaly

    hi , many thanks for your tutorial , and thanks ionic team to add this important component

    when i try this example it gives me an error

    EXCEPTION: Error: Uncaught (in promise): No provider for Content! (VirtualScroll -> Content)

    i tried to add provider like this :

    @Page({
    template: require(‘./medical_list.html’),
    providers: [VirtualScroll]
    })

    but it gives me the same error

    how can i solve this problem

  • TuanBachVan

    Hi. Virtual scroll is really cool. But I encounter a problem is after load virtual scroll and scroll down some pages, next I appended some items to the list . A scroll position is lost, I must scroll down again.
    Do you have any trick for this ?
    Thank you.

  • Jeremy Colton

    Looks great! I have 2 questions:

    1. My list has 2 items per row (like a grid). How can I modify your virtual-scroll to allow this?

    2. You say not to update the data-set, but my list will change regularly as new data is available from the server. Should I use a “pull-to-refresh” instead of trying to add and remove elements from the list?

    • Omkar

      Hey Jeremy,

      Have you got a solution for the first point you mentioned ?

  • Filipe Freire

    Hi Josh love the tutorial. One question can i use this with cards ?

    • berardo

      Looks like there are problems rendering and scrolling content in ion-cards.
      You might achieve same results avoiding ion-cards to wrap virtualscroll, but extending ion-card style in ion-list.
      Something like:
      ion-list.mylist { @extend ion-card; }

      • Roger Saner

        @berardo:disqus thanks, that’s super helpful. Do you mind posting a snippet of what your html looks like? I can’t quite figure out how you’ve set this up. I thought you might mean it’s but surely it’s meant to be ?

  • masimak

    “Avoid changing the dataset used to generate the list, as the entire virtual scroll will need to be reset and this is a costly operation”… What happened to using immutable lists to improve performance? This seems like having to choose between angular2 CD performance vs Ionic2 rendering performance. Should we have both?

  • Pingback: An Introduction to Lists in Ionic 2 | HTML5 Mobile Tutorials | Ionic, Phaser, Sencha Touch & PhoneGap()

  • Pingback: An Introduction to Lists in Ionic 2 | joshmorony - Build Mobile Apps with HTML5()

  • willburt

    Great post as always Josh 🙂 I’ve found a small change needed for the current version of Ionic 2. The ion-items within the ion-list now needs to be set up like this

    rather than

    Keep up the good work!

  • Саша Завалий
  • Fernando

    Hola. Gracias por compartir

    Estoy haciendo una APP, y tengo un listado y quiero que por ejemplo se carguen primero 20 item y mientras voy bajando se vayan cargando por ejemplo de 5 en sinco

  • JB

    Hi,
    Could someone see this topic about this?

    https://forum.ionicframework.com/t/virtual-scroll-misses-last-item-2-0-0-rc4/74115

    Virtual Scroll does not work fine on 2.0.0 RC4, the last item disappear when I change the dataset.

    Regards!