Virtualization in MultiSelect component

8 Oct 202524 minutes to read

MultiSelect virtualization is a performance optimization technique that efficiently renders large lists by creating only a fixed number of DOM elements. This approach is particularly beneficial when working with extensive datasets, as it maintains optimal performance by reusing existing DOM elements during scrolling rather than creating new elements for each item. The recycling process operates automatically behind the scenes.

During virtual scrolling, data retrieval from the data source depends on the popup height and list item height calculations. Enable the enableVirtualization property to activate this virtualization technique in the MultiSelect component.

When data is fetched from the data source, the actionBegin event triggers before data retrieval starts, followed by the actionComplete event upon successful data retrieval.

The MultiSelect component supports incremental search with virtualization. When a key is typed, focus moves to the corresponding element in the open popup state. In the closed popup state, the popup opens and focus moves to the corresponding element based on the typed key. Incremental search functionality works seamlessly with remote data binding scenarios.

Binding local data

The MultiSelect component generates list items from an array of complex data by mapping appropriate columns to the fields property. With virtual scrolling enabled, the list updates based on scroll offset values, triggering requests to fetch additional data as needed.

In the following example, the id column maps to the value field and the text column maps to the text field from complex data.

import { NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { FormsModule } from '@angular/forms'
import { MultiSelectModule } from '@syncfusion/ej2-angular-dropdowns'



import { Component } from '@angular/core';
import { MultiSelectComponent, VirtualScroll } from '@syncfusion/ej2-angular-dropdowns';

MultiSelectComponent.Inject(VirtualScroll);

@Component({
imports: [
        FormsModule,MultiSelectModule
    ],


standalone: true,
    selector: 'app-root',
    // specifies the virtual-scroll url path  
    templateUrl: 'virtual-scroll.html'
})
export class AppComponent {
public records: { [key: string]: Object }[] = [];
    constructor() {
        for (let i: number = 1; i <= 150; i++) {
            const item: { [key: string]: Object } = {
                id: 'id' + i,
                text: `Item ${i}`,
            };
            this.records.push(item);
        }
    }
    // maps the appropriate column to fields property
    public fields: object = { text: 'text', value: 'id' };
    // set the placeholder to AutoComplete input
    public waterMark: string = 'e.g. Item 1';   
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));
<div id="wrapper" style='margin-top: 20px'>
	  <div id='content' style="margin: 0px auto; width:300px;">
        <!-- specifies the virtualization for the MultiSelect component-->
        <ejs-multiselect id='multiselect-virtulization' [dataSource]='records' [fields]='fields' popupHeight='200px' [enableVirtualization]='true' [allowFiltering]='false' [placeholder]='waterMark'>
        </ejs-multiselect>
      </div>
    </div>

Binding remote data

The MultiSelect component retrieves data from remote data services using the DataManager component, triggering actionBegin and actionComplete events to update list data. During virtual scrolling, additional data requests from the server also trigger these events.

The following sample displays OrderId values from the Orders Data Service.

import { NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { FormsModule } from '@angular/forms'
import { MultiSelectModule } from '@syncfusion/ej2-angular-dropdowns'
import { Component } from '@angular/core';
import { MultiSelectComponent, VirtualScroll } from '@syncfusion/ej2-angular-dropdowns';
import { Query, DataManager, UrlAdaptor } from '@syncfusion/ej2-data';

MultiSelectComponent.Inject(VirtualScroll);

@Component({
imports: [
        FormsModule,MultiSelectModule
    ],


standalone: true,
    selector: 'app-root',
    // specifies the virtual-scroll url path  
    templateUrl: 'virtual-scroll.html'
})
export class AppComponent {
    // bind the DataManager instance to dataSource property
    public customerData: DataManager = new DataManager({
        url: 'https://services.syncfusion.com/angular/production/api/VirtualDropdownData',
        adaptor: new UrlAdaptor,
        crossDomain: true
    });
    // maps the appropriate column to fields property
    public customerField: { [key: string]: string } = { text: 'OrderID', value: 'OrderID' };
    // set the placeholder to AutoComplete input
    public waterMark: string = 'OrderID ';   
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));
<div id="wrapper" style='margin-top: 20px'>
	  <div id='content' style="margin: 0px auto; width:300px;">
        <!-- specifies the virtualization for the MultiSelect component-->
        <ejs-multiselect id='multiselect-virtulization' [dataSource]='customerData' [fields]='customerField' popupHeight='200px' [enableVirtualization]='true' [allowFiltering]='true' [placeholder]='waterMark'>
        </ejs-multiselect>
      </div>
    </div>

Customizing items count in virtualization

When enableVirtualization is enabled, the component considers the take property value provided in the Query parameter during initial state or actionBegin event. The component internally calculates items that fit within the current page (typically twice the popup height). If the user-provided take value is less than the minimum number of items that fit into the popup, the component ignores the user-provided value.

The following sample demonstrates customizing items count in virtualization.

import { NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { FormsModule } from '@angular/forms'
import { MultiSelectModule } from '@syncfusion/ej2-angular-dropdowns'



import { Component } from '@angular/core';
import { MultiSelectComponent, VirtualScroll } from '@syncfusion/ej2-angular-dropdowns';
import { Query } from '@syncfusion/ej2-data';

MultiSelectComponent.Inject(VirtualScroll);

@Component({
imports: [
        FormsModule,MultiSelectModule
    ],


standalone: true,
    selector: 'app-root',
    // specifies the virtual-scroll url path  
    templateUrl: 'virtual-scroll.html'
})
export class AppComponent {
public records: { [key: string]: Object }[] = [];
    constructor() {
        for (let i: number = 1; i <= 150; i++) {
            const item: { [key: string]: Object } = {
                id: 'id' + i,
                text: `Item ${i}`,
            };
            this.records.push(item);
        }
    }
    // maps the appropriate column to fields property
    public fields: object = { text: 'text', value: 'id' };
    public query: Query = new Query().take(40);
    public onBegin: any = (e: any) => {
        e.query = new Query().take(45);
    };
    // set the placeholder to AutoComplete input
    public waterMark: string = 'e.g. Item 1';   
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));
<div id="wrapper" style='margin-top: 20px'>
	  <div id='content' style="margin: 0px auto; width:300px;">
        <!-- specifies the virtualization for the MultiSelect component-->
        <ejs-multiselect id='multiselect-virtulization' [dataSource]='records' [fields]='fields' popupHeight='200px'  [query]='query' (actionBegin)='onBegin($event)' [enableVirtualization]='true' [allowFiltering]='false' [placeholder]='waterMark'>
        </ejs-multiselect>
      </div>
    </div>

Grouping with virtualization

The MultiSelect component supports grouping with virtualization, allowing organization of elements into categories. Each list item can be classified using the groupBy field in the data table. After grouping, virtualization operates similarly to local data binding for seamless user experience. When bound to remote data, an initial request retrieves all data for grouping purposes, after which the grouped data functions like local data binding with virtualization.

The following sample demonstrates grouping with virtualization.

import { NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { FormsModule } from '@angular/forms'
import { MultiSelectModule } from '@syncfusion/ej2-angular-dropdowns'



import { Component } from '@angular/core';
import { MultiSelectComponent, VirtualScroll } from '@syncfusion/ej2-angular-dropdowns';

MultiSelectComponent.Inject(VirtualScroll);

@Component({
imports: [
        FormsModule,MultiSelectModule
    ],


standalone: true,
    selector: 'app-root',
    // specifies the virtual-scroll url path  
    templateUrl: 'virtual-scroll.html'
})
export class AppComponent {
public records: { [key: string]: Object }[] = [];
    constructor() {
        for (let i = 1; i <= 150; i++) {
            let id = 'id' + i;
            let text = `Item ${i}`;
            let group = 'Group A';

            // Generate a random number between 1 and 4 to determine the group
            const randomGroup = Math.floor(Math.random() * 4) + 1;
            switch (randomGroup) {
                case 1:
                    group = 'Group A';
                    break;
                case 2:
                    group = 'Group B';
                    break;
                case 3:
                    group = 'Group C';
                    break;
                case 4:
                    group = 'Group D';
                    break;
                default:
                    break;
            }
            this.records.push({id, text, group});
        }
    }
    // maps the appropriate column to fields property
    public fields: object = { groupBy: 'group', text: 'text', value: 'id' };
    // set the placeholder to AutoComplete input
    public waterMark: string = 'e.g. Item 1';   
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));
<div id="wrapper" style='margin-top: 20px'>
	  <div id='content' style="margin: 0px auto; width:300px;">
        <!-- specifies the virtualization for the MultiSelect component-->
        <ejs-multiselect id='multiselect-virtulization-grouping' [dataSource]='records' [fields]='fields' popupHeight='200px' [enableVirtualization]='true' [allowFiltering]='true' [placeholder]='waterMark'>
        </ejs-multiselect>
      </div>
    </div>

Filtering with virtualization

The MultiSelect component supports filtering with virtualization through the built-in filtering feature enabled by the allowFiltering property. With virtual scrolling, filtering operates based on typed characters by sending requests to the server using the full data source. The component triggers an action event before initiating requests and an action complete event upon successful data retrieval. Initial data loads when the popup opens, and the popup closes regardless of filter list selection status.

The following sample demonstrates filtering with virtualization.

import { NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { FormsModule } from '@angular/forms'
import { MultiSelectModule } from '@syncfusion/ej2-angular-dropdowns'



import { Component } from '@angular/core';
import { MultiSelectComponent, VirtualScroll } from '@syncfusion/ej2-angular-dropdowns';

MultiSelectComponent.Inject(VirtualScroll);

@Component({
imports: [
        FormsModule,MultiSelectModule
    ],


standalone: true,
    selector: 'app-root',
    // specifies the virtual-scroll url path  
    templateUrl: 'virtual-scroll.html'
})
export class AppComponent {
public records: { [key: string]: Object }[] = [];
    constructor() {
        for (let i: number = 1; i <= 150; i++) {
            const item: { [key: string]: Object } = {
                id: 'id' + i,
                text: `Item ${i}`,
            };
            this.records.push(item);
        }
    }
    // maps the appropriate column to fields property
    public fields: object = { text: 'text', value: 'id' };
    // set the placeholder to AutoComplete input
    public waterMark: string = 'e.g. Item 1';   
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));
<div id="wrapper" style='margin-top: 20px'>
	  <div id='content' style="margin: 0px auto; width:300px;">
        <!-- specifies the virtualization for the MultiSelect component-->
        <ejs-multiselect id='multiselect-virtulization' [dataSource]='records' [fields]='fields' popupHeight='200px' [enableVirtualization]='true' [allowFiltering]='true' [placeholder]='waterMark'>
        </ejs-multiselect>
      </div>
    </div>

Checkbox with virtualization

The MultiSelect component supports checkbox selection with virtualization through integrated functionality that enables multiple value selection using checkboxes when the mode property is set to CheckBox. With virtual scrolling, checkboxes render with each list element, and the component value property updates with respective values based on checkbox selection and deselection.

The following sample demonstrates checkbox with virtualization.

import { NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { FormsModule } from '@angular/forms'
import { MultiSelectModule } from '@syncfusion/ej2-angular-dropdowns'



import { Component } from '@angular/core';
import { MultiSelectComponent, VirtualScroll, CheckBoxSelectionService } from '@syncfusion/ej2-angular-dropdowns';

MultiSelectComponent.Inject(VirtualScroll);

@Component({
imports: [
        FormsModule,MultiSelectModule
    ],


standalone: true,
    selector: 'app-root',
    // specifies the virtual-scroll url path  
    templateUrl: 'virtual-scroll.html',
    providers: [CheckBoxSelectionService]
})
export class AppComponent {
public records: { [key: string]: Object }[] = [];
    constructor() {
        for (let i: number = 1; i <= 150; i++) {
            const item: { [key: string]: Object } = {
                id: 'id' + i,
                text: `Item ${i}`,
            };
            this.records.push(item);
        }
    }
    // maps the appropriate column to fields property
    public fields: object = { text: 'text', value: 'id' };
    public mode?: string = 'CheckBox';

    // set the placeholder to AutoComplete input
    public waterMark: string = 'e.g. Item 1';   
    ngOnInit(): void {
        // set the type of mode for checkbox to visualized the checkbox added in li element.
        this.mode = 'CheckBox';
    }
    
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));
<div id="wrapper" style='margin-top: 20px'>
	  <div id='content' style="margin: 0px auto; width:300px;">
        <!-- specifies the virtualization for the MultiSelect component-->
        <ejs-multiselect id='multiselect-virtulization' [dataSource]='records' [fields]='fields' popupHeight='200px' [mode]='mode' [enableVirtualization]='true' [allowFiltering]='false' [placeholder]='waterMark'>
        </ejs-multiselect>
      </div>
    </div>

Custom value with virtualization

The MultiSelect component supports custom values with virtualization. When the allowCustomValue property is enabled, users can add new options not currently available in the component values. Upon selecting newly added custom values, the MultiSelect triggers the customValueSelection event and appends the custom value to the end of the complete list.

The following sample demonstrates custom value with virtualization.

import { NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { FormsModule } from '@angular/forms'
import { MultiSelectModule } from '@syncfusion/ej2-angular-dropdowns'



import { Component } from '@angular/core';
import { MultiSelectComponent, VirtualScroll } from '@syncfusion/ej2-angular-dropdowns';

MultiSelectComponent.Inject(VirtualScroll);

@Component({
imports: [
        FormsModule,MultiSelectModule
    ],


standalone: true,
    selector: 'app-root',
    // specifies the virtual-scroll url path  
    templateUrl: 'virtual-scroll.html'
})
export class AppComponent {
public records: { [key: string]: Object }[] = [];
    constructor() {
        for (let i: number = 1; i <= 150; i++) {
            const item: { [key: string]: Object } = {
                id: 'id' + i,
                text: `Item ${i}`,
            };
            this.records.push(item);
        }
    }
    // maps the appropriate column to fields property
    public fields: object = { text: 'text', value: 'id' };
    // set the placeholder to AutoComplete input
    public waterMark: string = 'e.g. Item 1';   
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));
<div id="wrapper" style='margin-top: 20px'>
	  <div id='content' style="margin: 0px auto; width:300px;">
        <!-- specifies the virtualization for the MultiSelect component-->
        <ejs-multiselect id='multiselect-virtulization' [dataSource]='records' [fields]='fields' popupHeight='200px' [enableVirtualization]='true' [allowFiltering]='false' [allowCustomValue]="true" [placeholder]='waterMark'>
        </ejs-multiselect>
      </div>
    </div>

Preselect values with virtualization

The MultiSelect component supports preselected values with virtualization. When binding values from local or remote data to the MultiSelect component, the corresponding data value is retrieved from the server and updated within the component. When binding custom values to the component, the value updates within the component and the bound custom value appends to the end of the complete list.

The following sample demonstrates preselect values with virtualization.

import { NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { FormsModule } from '@angular/forms'
import { MultiSelectModule } from '@syncfusion/ej2-angular-dropdowns'



import { Component } from '@angular/core';
import { MultiSelectComponent, VirtualScroll } from '@syncfusion/ej2-angular-dropdowns';

MultiSelectComponent.Inject(VirtualScroll);

@Component({
imports: [
        FormsModule,MultiSelectModule
    ],


standalone: true,
    selector: 'app-root',
    // specifies the virtual-scroll url path  
    templateUrl: 'virtual-scroll.html'
})
export class AppComponent {
public records: { [key: string]: Object }[] = [];
    constructor() {
        for (let i: number = 1; i <= 150; i++) {
            const item: { [key: string]: Object } = {
                id: 'id' + i,
                text: `Item ${i}`,
            };
            this.records.push(item);
        }
    }
    // maps the appropriate column to fields property
    public fields: object = { text: 'text', value: 'id' };
    public value = ['id11', 'id22', 'id33'];
    // set the placeholder to AutoComplete input
    public waterMark: string = 'e.g. Item 1';   
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));
<div id="wrapper" style='margin-top: 20px'>
	  <div id='content' style="margin: 0px auto; width:300px;">
        <!-- specifies the virtualization for the MultiSelect component-->
        <ejs-multiselect id='multiselect-virtulization' [dataSource]='records' [value]='value'  [fields]='fields' popupHeight='200px' [enableVirtualization]='true' [allowFiltering]='false' [placeholder]='waterMark'>
        </ejs-multiselect>
      </div>
    </div>