What is Virtual Scrolling?
Modern web application are complex. They have a lot of moving parts, complexity and often times they have to deal with huge amount of data.
Consider that you have application in which you have to display a list of users, a very common use case.
As the number of items in the list increase so does number of elements in DOM resulting in more memory and cpu usage.
We can reduce memory and cpu consumption by rendering only limited set of all the items, we can determine this limited set by looking at the height of container, scroll position and height of individual item and then perform some calculations which tell us which items from the list should rendered in DOM and as soon user scroll we again perform this calculation, remove previously rendered items and render new items according to calculation. All of this sounds very complex and it is, but the good news is that Angular Material CDK Virtual Scroll does this all for you and then some more. From now on I will be referring Angular Material CDK Virtual Scroll as Virtual Scroll
So let's get started!
Prerequisites
Before we begin we need a sample application with list of data so we can play with it and later on add angular virtual scroll to it. I have created a small example
This example is built on using Angular version 8. It uses fake to generate fake data, add it to array and uses *ngFor
to render item of array as DOM elements in template.
Installation
Installing Material CDK is pretty straight forward. If you are following along: In the stackblitz demo click on dependencies, it is just beneath window where project file are listed see screen shot below, type @angular/cdk
and hit enter this will install material cdk. If you want to install it inside your angular cli project simply type npm i --save @angular/cdk
Once you have installed CDK, we are ready to move towards next step: using it!
Usage
Before we can begin using the Virtual Scroll we need to import the module for it. Let's import it. Open your app.module.ts
add this line after the last import
import { ScrollingModule } from '@angular/cdk/scrolling';
This will import the ScrollingModule
, now we need to tell our app.module.ts
to import contents of this module. For this inside the imports array add ScrollingModule
your app.module.ts
will look like this
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { ScrollingModule } from '@angular/cdk/scrolling';
@NgModule({
imports: [ BrowserModule, FormsModule, ScrollingModule ],
declarations: [ AppComponent ],
bootstrap: [ AppComponent ]
})
export class AppModule { }
Now we are ready to use Virtual Scroll!
We will modify our application to use Virtual Scroll! Here is link to stackblitz app with desired state. This is same as our initial app but with Angular CDK installed and imported.
Open app.component.ts
, inside the constructor of AppComponent
you will see
constructor() {
this.persons = Array(100).fill(0).map(() => {
return {
name: faker.name.findName(),
bio: faker.hacker.phrase(),
avatar: faker.image.business()
}
})
}
What this does is creates an array of 100 objects, each object contains name, bio and avatar generated by faker
. This array is assigned to instance variable called persons
Now open the template file for this component (app.component.html
).
<div class="search-wrapper cf">
<input type="text">
<button (click)="undefined">Go To</button>
</div>
<div *ngFor="let person of persons;let i = index">
<div class="card">
<img src="https://i.imgur.com/63S0RAq.png" alt="Avatar">
<div class="container">
<h4><b>{{person.name}}</b></h4>
<h4><b>ID: {{i}}</b></h4>
<p>{{person.bio.substr(0, 30)}}</p>
</div>
</div>
</div>
The template consist of a button with click handler defined as of now it does nothing. Below the button we are using *ngFor
to iterate over person array, for each person array we create a div with class card. This card
div consist person's name, id, bio and avatar. We are displaying only first 30 characters of bio.
Now let's modify our template to use Virtual Scroll. For this first we need to wrap our ngFor with <cdk-virtual-scroll-viewport>
and then replace *ngFor
with . cdk-virtual-scroll-viewport
has a required input called [itemSize]
.
itemSize
represents the height of item in pixels which we are rendering in our case this should be exact height of our card div which is 141px
. Another thing to note is that height of all of our cards component should be same. As of now the Virtual Scroll does not support fully support variable height. We will look into matter of variables heights later.
so lets wrap our *ngFor
with cdk-virtual-scroll-viewport
and replace our *ngFor
with *cdkVirtualFor
. Another thing which needs to be done before we see any visual change is giving height to our cdk-virtual-scroll-viewport
I want to show two user cards at once so I will give cdk-virtual-scroll-viewport
height of 282px
(size of single card is 141, 141 * 2 = 282) you can either give height in stylesheet our add style tag. I will be adding height via style tag
<cdk-virtual-scroll-viewport [itemSize]="141" style="height: 282px">
<div *cdkVirtualFor="let person of persons;let i = index">
<div class="card">
<img src="https://i.imgur.com/63S0RAq.png" alt="Avatar">
<div class="container">
<h4><b>{{person.name}}</b></h4>
<h4><b>ID: {{i}}</b></h4>
<p>{{person.bio.substr(0, 30)}}...</p>
</div>
</div>
</div>
</cdk-virtual-scroll-viewport>
Now we can see that our user list items are rendered. Lets verify that we DOM items are created dynamically and reused. Open your browser developers tools, select Elements Panel
. Find the DOM element shown in screen shot below and expand it.
Now if you scroll through the list you will see that limited number of DOM elements are created, which leads to reduce memory and cpu usage.
Items with variable heights
As of now Virtual Scroll does not support variable heights, but it is being worked on and is in experimental phase which means you should not use it in production as its api may change.
For items with variable height we need to install @angular/cdk-experimental
. You can install into stackblitz by click on dependencies and type @angular/cdk-experimental
and hit enter. It will install the experimental cdk for you. User of angular cli can install it by npm @angular/cdk-experimental
.
Now we need to import it into our app module. Just like before, add following line in app.module.ts
import { ScrollingModule as ExperimentalScrollingModule} from '@angular/cdk-experimental/scrolling';
It will import ScrollingModule
and rename it as ExperimentalScrollingModule
. We need both ScrollingModule
and ExperimentalScrollingModule
for variable height to work. Now add ExperimentalScrollingModule
into import array. You app.module.ts
should look like this:
{ NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { ScrollingModule } from '@angular/cdk/scrolling';
import { ScrollingModule as ExperimentalScrollingModule} from '@angular/cdk-experimental/scrolling';
@NgModule({
imports: [ BrowserModule, FormsModule, ScrollingModule, ExperimentalScrollingModule ],
declarations: [ AppComponent ],
bootstrap: [ AppComponent ]
})
export class AppModule { }
Now open app.component.html
and replace [itemSize]
with autosize
. Thats it! Now you can elements of variable height in your Virtual Scroll but as said before this feature is experimental and should not be used. You can find example for autosize here
Top comments (0)