The dual list contains two ListView. This allows you to move list items from one list to another using the client-side events. This section explains how to integrate the ListView component to achieve dual list.
Here, two ListView components have been used to display the list items. An ej2-button is used to transfer data between the ListView, and a textbox is used to achieve the UI of filtering support.
The dual list supports:
In the ListView component, sorting is enabled using the sortOrder property, and the select event is triggered while selecting an item. Here, the select event is triggered to enable and disable button states.
concat
with the second ListView. This button is enabled only when the data source
of the first ListView is not empty.dataManager
has been
used to fetch data from the data source and display in ListView.
import * as React from 'react';
import * as ReactDOM from "react-dom";
import { DataManager, Query } from '@syncfusion/ej2-data';
import { enableRipple } from "@syncfusion/ej2-base";
import { ListViewComponent } from '@syncfusion/ej2-react-lists';
import { ButtonComponent } from '@syncfusion/ej2-react-buttons';
enableRipple(true);
export default class App extends React.Component {
constructor() {
super(...arguments);
//Define an array of JSON data
this.firstListData = [
{ text: "Hennessey Venom", id: "list-01" },
{ text: "Bugatti Chiron", id: "list-02" },
{ text: "Bugatti Veyron Super Sport", id: "list-03" },
{ text: "SSC Ultimate Aero", id: "list-04" },
{ text: "Koenigsegg CCR", id: "list-05" },
{ text: "McLaren F1", id: "list-06" }
];
this.secondListData = [
{ text: "Aston Martin One- 77", id: "list-07" },
{ text: "Jaguar XJ220", id: "list-08" },
{ text: "McLaren P1", id: "list-09" },
{ text: "Ferrari LaFerrari", id: "list-10" }
];
this.firstInput = null;
this.secondInput = null;
this.listFirstInstance = null;
this.listSecondInstance = null;
this.firstBtnInstance = null;
this.secondBtnInstance = null;
this.thirdBtnInstance = null;
this.fourthBtnInstance = null;
//Map the appropriate columns to fields property
this.fields = { text: "text", id: "id" };
}
componentDidMount() {
if (this.listFirstInstance && this.listSecondInstance) {
this.firstListData = this.listFirstInstance.dataSource.slice();
this.secondListData = this.listSecondInstance.dataSource.slice();
}
}
onFirstListSelect() {
if (this.secondBtnInstance)
this.secondBtnInstance.disabled = false;
}
onSecondListSelect() {
if (this.thirdBtnInstance)
this.thirdBtnInstance.disabled = false;
}
updateFirstListData() {
if (this.listFirstInstance) {
Array.prototype.forEach.call(this.listFirstInstance.liCollection, (list) => {
this.firstListData.forEach((data, index) => {
if (list.innerText.trim() === data.text) {
delete this.firstListData[index];
}
});
});
if (this.firstInput) {
this.firstInput.value = "";
}
let ds = [];
this.firstListData.forEach(data => {
ds.push(data);
});
this.firstListData = ds;
}
}
updateSecondListData() {
Array.prototype.forEach.call(this.listSecondInstance.liCollection, (list) => {
this.secondListData.forEach((data, index) => {
if (list.innerText.trim() === data.text) {
delete this.secondListData[index];
}
});
});
if (this.secondInput) {
this.secondInput.value = "";
}
let ds = [];
this.secondListData.forEach(data => {
ds.push(data);
});
this.secondListData = ds;
}
onFirstKeyUp(e) {
if (this.firstInput) {
let value = this.firstInput.value;
var data = new DataManager(this.firstListData).executeLocal(new Query().where("text", "startswith", value, true));
if (this.listFirstInstance) {
if (!value) {
this.listFirstInstance.dataSource = this.firstListData.slice();
}
else {
this.listFirstInstance.dataSource = data;
}
this.listFirstInstance.dataBind();
}
}
}
onSecondKeyUp(e) {
if (this.secondInput) {
let value = this.secondInput.value;
var data = new DataManager(this.secondListData).executeLocal(new Query().where("text", "startswith", value, true));
if (this.listSecondInstance) {
if (!value) {
this.listSecondInstance.dataSource = this.secondListData.slice();
}
else {
this.listSecondInstance.dataSource = data;
}
this.listSecondInstance.dataBind();
}
}
}
setButtonState() {
if (this.listFirstInstance &&
this.firstBtnInstance &&
this.secondBtnInstance &&
this.listSecondInstance &&
this.fourthBtnInstance &&
this.thirdBtnInstance) {
if (this.listFirstInstance.dataSource.length) {
this.firstBtnInstance.disabled = false;
}
else {
this.firstBtnInstance.disabled = true;
this.secondBtnInstance.disabled = true;
}
if (this.listSecondInstance.dataSource.length) {
this.fourthBtnInstance.disabled = false;
}
else {
this.fourthBtnInstance.disabled = true;
this.thirdBtnInstance.disabled = true;
}
}
}
firstBtnClick() {
if (this.listFirstInstance && this.firstBtnInstance && this.listSecondInstance) {
this.listSecondInstance.dataSource = Array.prototype.concat.call(this.listFirstInstance.dataSource, this.listSecondInstance.dataSource);
this.updateFirstListData();
this.listFirstInstance.removeMultipleItems(this.listFirstInstance.liCollection);
this.firstListData = this.firstListData.concat(this.listFirstInstance.dataSource);
this.secondListData = this.listSecondInstance.dataSource.slice();
this.firstBtnInstance.disabled = true;
this.onFirstKeyUp(null);
this.setButtonState();
}
}
secondBtnClick() {
if (this.listFirstInstance && this.secondBtnInstance && this.listSecondInstance) {
let e = this.listFirstInstance.getSelectedItems();
if (e) {
this.listSecondInstance.dataSource = Array.prototype.concat.call(this.listSecondInstance.dataSource, e.data);
this.listFirstInstance.removeItem(e.item);
this.firstListData = this.listFirstInstance.dataSource;
this.secondListData = this.listSecondInstance.dataSource.slice();
this.onFirstKeyUp(null);
this.secondBtnInstance.disabled = true;
this.setButtonState();
}
}
}
thirdBtnClick() {
if (this.listFirstInstance && this.listSecondInstance && this.thirdBtnInstance) {
let e = this.listSecondInstance.getSelectedItems();
if (e) {
this.listFirstInstance.dataSource = Array.prototype.concat.call(this.listFirstInstance.dataSource, e.data);
this.listSecondInstance.removeItem(e.item);
this.secondListData = this.listSecondInstance.dataSource;
this.firstListData = this.listFirstInstance.dataSource.slice();
this.onSecondKeyUp(null);
this.thirdBtnInstance.disabled = true;
this.setButtonState();
}
}
}
fourthBtnClick() {
if (this.listFirstInstance && this.listSecondInstance && this.fourthBtnInstance) {
this.listFirstInstance.dataSource = Array.prototype.concat.call(this.listFirstInstance.dataSource, this.listSecondInstance.dataSource);
this.updateSecondListData();
this.listSecondInstance.removeMultipleItems(this.listSecondInstance.liCollection);
this.secondListData = this.secondListData.concat(this.listSecondInstance.dataSource);
this.firstListData = this.listFirstInstance.dataSource.slice();
this.onSecondKeyUp(null);
this.setButtonState();
}
}
firstInputKeyUp() {
if (this.listFirstInstance &&
this.firstBtnInstance &&
this.secondBtnInstance &&
this.listSecondInstance &&
this.fourthBtnInstance &&
this.thirdBtnInstance &&
this.firstInput) {
let value = this.firstInput.value;
var data = new DataManager(this.firstListData).executeLocal(new Query().where("text", "startswith", value, true));
if (!value) {
this.listFirstInstance.dataSource = this.firstListData.slice();
}
else {
this.listFirstInstance.dataSource = data;
}
this.listFirstInstance.dataBind();
}
}
secondInputKeyUp() {
if (this.listFirstInstance &&
this.firstBtnInstance &&
this.secondBtnInstance &&
this.listSecondInstance &&
this.fourthBtnInstance &&
this.thirdBtnInstance &&
this.secondInput) {
let value = this.secondInput.value;
var data = new DataManager(this.secondListData).executeLocal(new Query().where("text", "startswith", value, true));
if (!value) {
this.listSecondInstance.dataSource = this.secondListData.slice();
}
else {
this.listSecondInstance.dataSource = data;
}
this.listSecondInstance.dataBind();
}
}
render() {
return (<div id="container">
<div className="col-lg-12 control-section">
<div>
<h3>Dual List</h3>
<div id="text1">
<input className="e-input" type="text" id="firstInput" ref={(inputRef) => (this.firstInput = inputRef)} placeholder="Filter" onKeyUp={this.firstInputKeyUp.bind(this)} title="Type in a name"/>
</div>
<ListViewComponent id="list-1" dataSource={this.firstListData} fields={this.fields} sortOrder="Ascending" select={this.onFirstListSelect.bind(this)} ref={scope => {
this.listFirstInstance = scope;
}}/>
<div id="btn">
<ButtonComponent onClick={this.firstBtnClick.bind(this)} ref={button1 => {
this.firstBtnInstance = button1;
}}>
{" "}
>>{" "}
</ButtonComponent>
<ButtonComponent onClick={this.secondBtnClick.bind(this)} ref={button2 => {
this.secondBtnInstance = button2;
}}>
{" "}
>{" "}
</ButtonComponent>
<ButtonComponent onClick={this.thirdBtnClick.bind(this)} ref={button3 => {
this.thirdBtnInstance = button3;
}}>
{" "}
{"<"}{" "}
</ButtonComponent>
<ButtonComponent onClick={this.fourthBtnClick.bind(this)} ref={button4 => {
this.fourthBtnInstance = button4;
}}>
{" "}
{"<<"}{" "}
</ButtonComponent>
</div>
<div id="text2">
<input className="e-input" type="text" onKeyUp={this.secondInputKeyUp.bind(this)} id="secondInput" ref={(inputRef) => (this.secondInput = inputRef)} placeholder=" Filter" title="Type in a name"/>
</div>
<ListViewComponent id="list-2" dataSource={this.secondListData} fields={this.fields} sortOrder="Ascending" select={this.onSecondListSelect.bind(this)} ref={list => {
this.listSecondInstance = list;
}}/>
</div>
</div>
</div>);
}
}
ReactDOM.render(<App />, document.getElementById('element'));
<!DOCTYPE html>
<html lang="en">
<head>
<title>Syncfusion React ListView</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="Essential JS 2 for React Components" />
<meta name="author" content="Syncfusion" />
<link href="//cdn.syncfusion.com/ej2/20.1.55/ej2-base/styles/material.css" rel="stylesheet" />
<link href="//cdn.syncfusion.com/ej2/20.1.55/ej2-react-lists/styles/material.css" rel="stylesheet" />
<link href="//cdn.syncfusion.com/ej2/20.1.55/ej2-react-buttons/styles/material.css" rel="stylesheet" />
<link href="//cdn.syncfusion.com/ej2/20.1.55/ej2-react-inputs/styles/material.css" rel="stylesheet" />
<link href="index.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.38/system.js"></script>
<script src="systemjs.config.js"></script>
<style>
#loader {
color: #008cff;
height: 40px;
left: 45%;
position: absolute;
top: 45%;
width: 30%;
}
</style>
</head>
<body>
<div id='element' style="margin:0 auto; max-width:500px;">
<div id='loader'>Loading....</div>
</div>
</body>
</html>
#loader {
color: #008cff;
height: 40px;
width: 30%;
position: absolute;
font-family: 'Helvetica Neue', 'calibiri';
font-size: 14px;
top: 45%;
left: 45%;
}
#list-1,
#list-2 {
width: 40%;
height: 430px;
box-shadow: 0 1px 4px #ddd;
border-bottom: 1px solid #ddd;
}
#firstList, #secondList {
margin-top: 13px;
}
.e-btn {
width: 60px;
height: 60px;
margin-bottom: 15px;
}
#btn {
float: left;
width: 5%;
padding-left: 20px;
margin-top: 67px;
}
#list-1 {
float: left;
}
#list-2 {
float: right;
}
#firstInput {
width: 40%;
}
#secondInput {
width: 52%;
margin-left: 14%;
}
#text2 {
margin-top: -30px;
margin-left: 115px;
}
import * as React from 'react';
import * as ReactDOM from "react-dom";
import { DataManager, Query } from '@syncfusion/ej2-data';
import { enableRipple } from "@syncfusion/ej2-base";
import { ListViewComponent } from '@syncfusion/ej2-react-lists';
import { ButtonComponent } from '@syncfusion/ej2-react-buttons';
enableRipple(true);
export default class App extends React.Component<{}, {}> {
//Define an array of JSON data
firstListData: any[] = [
{ text: "Hennessey Venom", id: "list-01" },
{ text: "Bugatti Chiron", id: "list-02" },
{ text: "Bugatti Veyron Super Sport", id: "list-03" },
{ text: "SSC Ultimate Aero", id: "list-04" },
{ text: "Koenigsegg CCR", id: "list-05" },
{ text: "McLaren F1", id: "list-06" }
];
secondListData: any[] = [
{ text: "Aston Martin One- 77", id: "list-07" },
{ text: "Jaguar XJ220", id: "list-08" },
{ text: "McLaren P1", id: "list-09" },
{ text: "Ferrari LaFerrari", id: "list-10" }
];
firstInput: HTMLInputElement | null = null;
secondInput: HTMLInputElement | null = null;
listFirstInstance: ListViewComponent | null = null;
listSecondInstance: ListViewComponent | null = null;
firstBtnInstance: ButtonComponent | null = null;
secondBtnInstance: ButtonComponent | null = null;
thirdBtnInstance: ButtonComponent | null = null;
fourthBtnInstance: ButtonComponent | null = null;
componentDidMount() {
if (this.listFirstInstance && this.listSecondInstance) {
this.firstListData = (this.listFirstInstance.dataSource as any[]).slice();
this.secondListData = (this.listSecondInstance.dataSource as any[]).slice();
}
}
//Map the appropriate columns to fields property
fields: Object = { text: "text", id: "id" };
onFirstListSelect() {
if (this.secondBtnInstance) this.secondBtnInstance.disabled = false;
}
onSecondListSelect() {
if (this.thirdBtnInstance) this.thirdBtnInstance.disabled = false;
}
updateFirstListData() {
if (this.listFirstInstance) {
Array.prototype.forEach.call(
(this.listFirstInstance as any).liCollection as any[],
(list: any) => {
this.firstListData.forEach((data, index) => {
if (list.innerText.trim() === data.text) {
delete this.firstListData[index];
}
});
}
);
if (this.firstInput) {
this.firstInput.value = "";
}
let ds: any[] = [];
this.firstListData.forEach(data => {
ds.push(data);
});
this.firstListData = ds;
}
}
updateSecondListData() {
Array.prototype.forEach.call((this.listSecondInstance as any).liCollection, (list: any) => {
this.secondListData.forEach((data, index) => {
if (list.innerText.trim() === data.text) {
delete this.secondListData[index];
}
});
});
if (this.secondInput) {
this.secondInput.value = "";
}
let ds: any[] = [];
this.secondListData.forEach(data => {
ds.push(data);
});
this.secondListData = ds;
}
onFirstKeyUp(e: any) {
if (this.firstInput) {
let value = this.firstInput.value;
var data = new DataManager(this.firstListData).executeLocal(
new Query().where("text", "startswith", value, true)
);
if (this.listFirstInstance) {
if (!value) {
this.listFirstInstance.dataSource = this.firstListData.slice();
} else {
this.listFirstInstance.dataSource = data as any[];
}
this.listFirstInstance.dataBind();
}
}
}
onSecondKeyUp(e: any) {
if (this.secondInput) {
let value = this.secondInput.value;
var data = new DataManager(this.secondListData).executeLocal(
new Query().where("text", "startswith", value, true)
);
if (this.listSecondInstance) {
if (!value) {
this.listSecondInstance.dataSource = this.secondListData.slice();
} else {
this.listSecondInstance.dataSource = data as any[];
}
this.listSecondInstance.dataBind();
}
}
}
setButtonState() {
if (
this.listFirstInstance &&
this.firstBtnInstance &&
this.secondBtnInstance &&
this.listSecondInstance &&
this.fourthBtnInstance &&
this.thirdBtnInstance
) {
if ((this.listFirstInstance.dataSource as any[]).length) {
this.firstBtnInstance.disabled = false;
} else {
this.firstBtnInstance.disabled = true;
this.secondBtnInstance.disabled = true;
}
if ((this.listSecondInstance.dataSource as any[]).length) {
this.fourthBtnInstance.disabled = false;
} else {
this.fourthBtnInstance.disabled = true;
this.thirdBtnInstance.disabled = true;
}
}
}
firstBtnClick() {
if (this.listFirstInstance && this.firstBtnInstance && this.listSecondInstance) {
this.listSecondInstance.dataSource = Array.prototype.concat.call(
this.listFirstInstance.dataSource,
this.listSecondInstance.dataSource
);
this.updateFirstListData();
this.listFirstInstance.removeMultipleItems((this.listFirstInstance as any).liCollection);
this.firstListData = this.firstListData.concat(this.listFirstInstance.dataSource);
this.secondListData = this.listSecondInstance.dataSource.slice();
this.firstBtnInstance.disabled = true;
this.onFirstKeyUp(null);
this.setButtonState();
}
}
secondBtnClick() {
if (this.listFirstInstance && this.secondBtnInstance && this.listSecondInstance) {
let e = this.listFirstInstance.getSelectedItems();
if (e) {
this.listSecondInstance.dataSource = Array.prototype.concat.call(
this.listSecondInstance.dataSource,
e.data
);
this.listFirstInstance.removeItem(e.item as any);
this.firstListData = this.listFirstInstance.dataSource as any[];
this.secondListData = this.listSecondInstance.dataSource.slice();
this.onFirstKeyUp(null);
this.secondBtnInstance.disabled = true;
this.setButtonState();
}
}
}
thirdBtnClick() {
if (this.listFirstInstance && this.listSecondInstance && this.thirdBtnInstance) {
let e = this.listSecondInstance.getSelectedItems();
if (e) {
this.listFirstInstance.dataSource = Array.prototype.concat.call(
this.listFirstInstance.dataSource,
e.data
);
this.listSecondInstance.removeItem(e.item as any);
this.secondListData = this.listSecondInstance.dataSource as any[];
this.firstListData = this.listFirstInstance.dataSource.slice();
this.onSecondKeyUp(null);
this.thirdBtnInstance.disabled = true;
this.setButtonState();
}
}
}
fourthBtnClick() {
if (this.listFirstInstance && this.listSecondInstance && this.fourthBtnInstance) {
this.listFirstInstance.dataSource = Array.prototype.concat.call(
this.listFirstInstance.dataSource,
this.listSecondInstance.dataSource
);
this.updateSecondListData();
this.listSecondInstance.removeMultipleItems((this.listSecondInstance as any).liCollection);
this.secondListData = this.secondListData.concat(this.listSecondInstance.dataSource);
this.firstListData = this.listFirstInstance.dataSource.slice();
this.onSecondKeyUp(null);
this.setButtonState();
}
}
firstInputKeyUp() {
if (
this.listFirstInstance &&
this.firstBtnInstance &&
this.secondBtnInstance &&
this.listSecondInstance &&
this.fourthBtnInstance &&
this.thirdBtnInstance &&
this.firstInput
) {
let value = this.firstInput.value;
var data = new DataManager(this.firstListData).executeLocal(
new Query().where("text", "startswith", value, true)
);
if (!value) {
this.listFirstInstance.dataSource = this.firstListData.slice();
} else {
this.listFirstInstance.dataSource = data as any[];
}
this.listFirstInstance.dataBind();
}
}
secondInputKeyUp() {
if (
this.listFirstInstance &&
this.firstBtnInstance &&
this.secondBtnInstance &&
this.listSecondInstance &&
this.fourthBtnInstance &&
this.thirdBtnInstance &&
this.secondInput
) {
let value = this.secondInput.value;
var data = new DataManager(this.secondListData).executeLocal(
new Query().where("text", "startswith", value, true)
);
if (!value) {
this.listSecondInstance.dataSource = this.secondListData.slice();
} else {
this.listSecondInstance.dataSource = data as any[];
}
this.listSecondInstance.dataBind();
}
}
render() {
return (
<div id="container">
<div className="col-lg-12 control-section">
<div>
<h3>Dual List</h3>
<div id="text1">
<input
className="e-input"
type="text"
id="firstInput"
ref={(inputRef: any) => (this.firstInput = inputRef)}
placeholder="Filter"
onKeyUp={this.firstInputKeyUp.bind(this)}
title="Type in a name"
/>
</div>
<ListViewComponent
id="list-1"
dataSource={this.firstListData}
fields={this.fields}
sortOrder="Ascending"
select={this.onFirstListSelect.bind(this)}
ref={scope => {
this.listFirstInstance = scope;
}}
/>
<div id="btn">
<ButtonComponent
onClick={this.firstBtnClick.bind(this)}
ref={button1 => {
this.firstBtnInstance = button1;
}}
>
{" "}
>>{" "}
</ButtonComponent>
<ButtonComponent
onClick={this.secondBtnClick.bind(this)}
ref={button2 => {
this.secondBtnInstance = button2;
}}
>
{" "}
>{" "}
</ButtonComponent>
<ButtonComponent
onClick={this.thirdBtnClick.bind(this)}
ref={button3 => {
this.thirdBtnInstance = button3;
}}
>
{" "}
{"<"}{" "}
</ButtonComponent>
<ButtonComponent
onClick={this.fourthBtnClick.bind(this)}
ref={button4 => {
this.fourthBtnInstance = button4;
}}
>
{" "}
{"<<"}{" "}
</ButtonComponent>
</div>
<div id="text2">
<input
className="e-input"
type="text"
onKeyUp={this.secondInputKeyUp.bind(this)}
id="secondInput"
ref={(inputRef: any) => (this.secondInput = inputRef)}
placeholder=" Filter"
title="Type in a name"
/>
</div>
<ListViewComponent
id="list-2"
dataSource={this.secondListData}
fields={this.fields}
sortOrder="Ascending"
select={this.onSecondListSelect.bind(this)}
ref={list => {
this.listSecondInstance = list;
}}
/>
</div>
</div>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('element'));