Angular CDK Virtual Scroll
Tags: angular, cdk, virtualscroll, material
Published: 2019-07-27 09:26:39


# 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](https://stackblitz.com/edit/ngfor-faker)
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` 

![Installing CDK](https://i.imgur.com/ScWpEbn.png)

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](https://stackblitz.com/edit/ngfor-faker-material-cdk?file=src/app/app.component.html) 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.

![Virtual Scroll in dev tools](https://i.imgur.com/0dPEjTI.png)

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:
```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';
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](https://stackblitz.com/edit/cdk-virtual-scroll-autosize?file=src/app/app.module.ts)