January 17, 2023 07:09 by
Peter
Sometimes while working with web-based Angular applications you might find yourself in a situation where you need to render thousands of records inside a list. However, rendering that many records directly in the DOM can be a memory-expensive operation and can lead to performance issues or even can cause lag while scrolling through the list.
However, the Angular CDK library, fortunately, comes bundled with a feature called Virtual Scrolling that can be used to fix this issue. This component only the number of records that can fit in the viewport i.e. is visible to the user. And keeps changing the records as the user starts scrolling through the list. This feature was introduced with Angular CDK version 7.
Let's understand this technique in detail with an example.
First, create a dead simple Angular application using Angular CLI.
ng new sample
next, install the Angular CDK package either with Angular CLI or npm.
ng add @angular/cdk
or,
npm install @angular/cdk
Now first inside the app.component.ts file let's generate 500,000 rows and try to render it without the virtual scroll feature.
To generate an array we'll simply use the Array.from method.
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent {
rows: string[] = Array.from(Array(500000)).map((_, i) => `Row #${i}`);
}
Next, we'll try to render the rows in the template.
<div class="box">
<div class="row" *ngFor="let row of rows">{{ row }}</div>
</div>
We'll also add some styling to the CSS file of this template.
.box {
height: 250px;
width: 250px;
border: 1px solid #e3e3e3;
overflow: scroll;
}
.row {
height: 50px;
}
Immediately you'll notice that it is taking a while to render that many rows. And if you try to inspect the element you'll notice that the browser is literally rendering 500,000 rows in the DOM.
Now, let's do the same thing using the Virtual Scrolling technique.
First, we need to import the ScrollingModule inside the AppModule and add it to the imports array.
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { ScrollingModule } from '@angular/cdk/scrolling';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
BrowserAnimationsModule,
ScrollingModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Importing this module gives us access to cdk-virtual-scroll-viewport components and the cdkVirtualFor directive which we'll be using to implement the virtual scroll strategy.
<cdk-virtual-scroll-viewport itemSize="50" class="box">
<div class="row" *cdkVirtualFor="let row of rows">
{{ row }}
</div>
</cdk-virtual-scroll-viewport>
we're using cdkVirtualFor instead of the ngFor loop to iterate over the rows and itemSize is simply the height of individual elements which in our case is 50px (see the styling for .row class).
As soon as you complete the above implementation you'll notice the rendering time is reduced next to none, and if you inspect the DOM element you'll notice that component has rendered a very limited number of elements and it simply replaces existing rows with new rows as the user scrolls through the list.