Virtualization in MultiSelect Dropdown
25 Jul 202424 minutes to read
MultiSelect Dropdown virtualization is a technique used to efficiently render extensive lists of items while minimizing the impact on performance. This method is particularly advantageous when dealing with large datasets because it ensures that only a fixed number of DOM (Document Object Model) elements are created. When scrolling through the list, existing DOM elements are reused to display relevant data instead of generating new elements for each item. This recycling process is managed internally.
During virtual scrolling, the data retrieved from the data source depends on the popup height and the calculation of the list item height. Enabling the enableVirtualization option in a MultiSelect Dropdown activates this virtualization technique.
When fetching data from the data source, the actionBegin event is triggered before data retrieval begins. Then, the actionComplete event is triggered once the data is successfully fetched.
Furthermore, Incremental Search is supported with virtualization in the MultiSelect component. When a key is typed, the focus is moved to the respective element in the open popup state. In the closed popup state, the popup opens, and focus is moved to the respective element in the popup list based on the typed key. The Incremental Search functionality is well-suited for scenarios involving remote data binding.
Binding local data
The MultiSelect can generate its list items through an array of complex data. For this, the appropriate columns should be mapped to the fields property. When using virtual scrolling, the list updates based on the scroll offset value, triggering a request to fetch more data from the server.
In the following example, id
column and text
column from complex data have been mapped to the value
field and text
field, respectively.
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 supports the retrieval of data from remote data services with the help of the DataManager
component, triggering the actionBegin
and actionComplete
events, and then updating the list data. During virtual scrolling, additional data is retrieved from the server, triggering the actionBegin
and actionComplete
events at that time as well.
The following sample displays the OrderId 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 the enableVirtualization
property is enabled, the take
property provided by the user within the Query parameter at the initial state or during the actionBegin
event will be considered. Internally, it calculates the items that fit onto the current page (i.e., probably twice the amount of the popup’s height). If the user-provided take value is less than the minimum number of items that fit into the popup, the user-provided take value will not be considered.
The following sample shows the example for 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. It allows you to organize elements into groups based on different categories. Each item in the list can be classified using the groupBy field in the data table. After grouping, virtualization works similarly to local data binding, providing a seamless user experience. When the data source is bound to remote data, an initial request is made to retrieve all data for the purpose of grouping. Subsequently, the grouped data works in the same way as local data binding on virtualization.
The following sample shows the example for 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. The MultiSelect includes a built-in feature that enables data filtering when the allowFiltering
option is enabled. In the context of Virtual Scrolling, the filtering process operates in response to the typed characters. Specifically, the MultiSelect sends a request to the server, utilizing the full data source, to achieve filtering. Before initiating the request, an action event is triggered. Upon successful retrieval of data from the server, an action complete event is triggered. The initial data is loaded when the popup is opened. Whether the filter list has a selection or not, the popup closes.
The following sample shows the example for 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. The MultiSelect comes with integrated functionality that allows for the selection of multiple values using checkboxes when the mode
property is configured to CheckBox
. In the context of Virtual Scrolling, the checkbox render with each list element. based on the checkbox selection and unselection, component value property updated with respective values.
The following sample shows the example for 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 value with Virtualization. When the allowCustomValue
property is enabled, the MultiSelect enables users to include a new option not currently available in the component value. Upon selecting this newly added custom value, the MultiSelect triggers the customValueSelection
event and also custom value will be added to the end of the complete list.
The following sample shows the example for 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 extends its support for preselected values with Virtualization. When binding values from local or remote data to the MultiSelect component, the corresponding data value is fetched from the server and promptly updated within the component. Moreover, when binding a custom value to the component, the value is updated within the component, and the bound custom value is seamlessly appended to the end of the complete list.
The following sample shows the example for Preselect 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' };
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>