Virtual scrolling in a mat-select dropdown using a nested form group— Angular 11

It’s been a long time I have written an article. Can’t wait for you to read this one.

Okay! What and why’s of Virtual Scrolling in Angular it is!! Let’s get started

What is virtual scrolling?

Loading only the required amount of data to display to the end-user at a given point of time

Why do we need Virtual Scrolling?

Let’s suppose we have to load 10k records into a select dropdown from which the user can select a value(s). From the browser’s standpoint, all the 10k records are fed to the select drop-down at once, implies the time to taken to render the entire data set to the view is very high. This will lead to a non-responsive browser screen.

This is where the concept of Virtual Scrolling will come to our rescue. Instead of loading the entire data set into the browser at once, leading to long render times, Virtual Scrolling will only load the part of data only the user can see. We have to write code to calculate the total length of the viewport of the drop-down; length to display a record in the drop-down and finally the no. of records to be displayed at a single point of time when the drop-down is opened.

Therefore, the time taken by the browser to render these 4 records or any 4 records when the user scrolls down is very less compared to render all the 10k records at once. To notice the difference, we can profile the application.

Alright, I get it, enough of theory and let’s dive deep into the code 🌊

What do we need??

Once we have the project in place with Angular Material installed, let’s create a view in the app.component.html which renders a user registration form.

In this project, I have decided to use the Reactive Forms in combination with Angular Material form fields to build the ‘User Registration’ form to replicate close to the enterprise applications. We have a ‘user form’ which has a nested form group ‘address’ which has one of the form-controls as ‘zipCode’.

At this point, I have decided to create 10,000 records of 4 digits each ranging from 0000–9999 and feed to the mat-select ZipCode dropdown to recreate the page lag involved to render this huge data set in the Browser.

Note — The initial value of the zip code is set to ‘1000’

app.component.ts
The bottom part of the app.component.html code
User Registration Form view

If you’re a curious soul like me, see the performance report generated using the Profiler in the browser. Let’s profile the application and see the time taken to render the data into the zip codes drop-down. Right-click on your application tab, click on the inspect option, go to the performance tab, click on the record icon to start recording, click on the target select drop-down, scroll down the drop-down. Now, stop recording in the performance tab, from the charts produced during this process, look for the render time during the click on the select drop-down action. This should give you the statistics of the time taken by the browser to directly render all the 10k records into the select drop-down.

Profiling the zip code select dropdown.gif

At the end of the GIF, notice the time taken to render this data by the browser — 2766 ms. This will create a bad experience for the end-users using the application.

Also, think about what is the probability of a user scrolling all the way down to the end of the list? This is where we have to be smart and load only the portion data that is being viewed by the end-user. Yes, we have a name for this solution — Virtual Scrolling; load only the portion of the data based on the user actions.

Well, there are working examples available right now regarding the same topic. Why am I writing this article and what interested me to write this article in the first place? The combination of mat-select drop-down in a form group; and the ability to set an initial value when the page reloads were not so evident in the first place. The Angular Material documentation does not have an example of the Virtual Scrolling with MatSelect and it was tough to find good examples where I can render the select drop-down with an initial value set.

Let’s see the <cdk-virtual-scroll-viewport> component in action. Using this component to build the view, will significantly reduce the page lag as the rendering time is pretty low.

with cdk-virtual-scroll-viewport

Let’s follow the above steps mentioned to profile the application again to notice a significant performance improvement while rendering the page.

Profiling the page with Virtual scrolling in place

The time taken to render the page, in this case, is 78ms. Since we developers are performance enthusiasts, let’s calculate the overall improvement — (2776/ 78; render time w/o virtual scroll / render time with virtual scroll) by 35 times.

The challenge is, the documentation is not straight forward, and we have to solve a few puzzles to get the required functionality.

The first catch is we have to provide the <cdk-virtual-scroll-viewport> with a class (“virtual-scroll”), which sets the height of the drop-down. Otherwise, we will not be able to view the records in the drop-down

app.component.css

Now, we will be able to see the drop-down displaying the records and there will not be any lag. But there is a problem!! If you notice we are not able to see the initial value of ‘1000’ not being displayed when the page is refreshed.

Here comes our second catch, as only a portion of data is loaded to the view, in this case, only 5 values are displayed at a time if the initial value set to the select dropdown is not created in the view, i.e., if the <mat-option> with the value of ‘1000’ (initial value set in the TS file) is not created in the view, the select drop-down will not be able to set the initial value and we would not be able to see. To overcome this issue, we have to conditionally create the mat-option with the selected value of the zip code if any.

That’s it we’re done 😄.

Thank you so much for going through my work, hope it will help you resolve the challenge(s). You have a good one!

Resources:

  1. https://angular.io/guide/reactive-forms#creating-nested-form-groups
  2. https://material.angular.io/cdk/scrolling/api
  3. Video recording for GIFs — Quick time player (Macbook Pro)
  4. For code snippets — https://carbon.now.sh/ and VS code editor
  5. Lucid Chart