Create Dual List from ListView in React ListView Component
23 Jan 202524 minutes to read
The dual list contains two ListView components. This allows you to move list items from one list to another using client-side events. This section explains how to integrate the ListView component to achieve a dual list.
Use cases
- Stock exchanges of two different countries
- Job applications (skill sets)
Integration of Dual List
Here, two ListView components have been used to display the list items. An EJ2 Button is used to transfer data between the ListViews, and a textbox is used to achieve the UI of filtering support.
The dual list supports:
- Moving whole data from one list to another.
- Moving selected data from one list to another.
- Filtering the list by using a client-side typed character.
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.
Manipulating data
Moving whole data from the first list to the second list(»)
The entire data set can be moved from the first ListView to the second by clicking the first button. When clicking the button, all list items are sliced and connected with the second ListView. This button is enabled only when the data source of the first ListView is not empty.
Moving whole data from the second list to the first list(«)
The functionality of the second button is the same as above, but data is transferred from the second list to the first list. This button is enabled only when the data source of the second ListView is not empty.
Moving selected item from one list to another list (>) and (<)
The select
event is triggered when selecting a list item in the ListView. The selected items can be transferred between two lists. These buttons will be enabled when selecting an item in lists.
Filtering method
The filtering method is used to filter list items when typing a character in the text box. In this method, the DataManager
has been used to fetch data from the data source and display in ListView.
Sorting
List items can be sorted in the ListView component using the sortOrder
property. You can enable sorting in one ListView; in the same order, data can be transferred to another 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';
import './index.css';
enableRipple(true);
function App() {
React.useEffect(() => {
componentDidMount();
}, []);
//Define an array of JSON data
let 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" }
];
let 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" }
];
let firstInput = null;
let secondInput = null;
let listFirstInstance = null;
let listSecondInstance = null;
let firstBtnInstance = null;
let secondBtnInstance = null;
let thirdBtnInstance = null;
let fourthBtnInstance = null;
function componentDidMount() {
if (listFirstInstance && listSecondInstance) {
firstListData = listFirstInstance.dataSource.slice();
secondListData = listSecondInstance.dataSource.slice();
}
}
//Map the appropriate columns to fields property
let fields = { text: "text", id: "id" };
function onFirstListSelect() {
if (secondBtnInstance)
secondBtnInstance.disabled = false;
}
function onSecondListSelect() {
if (thirdBtnInstance)
thirdBtnInstance.disabled = false;
}
function updateFirstListData() {
if (listFirstInstance) {
Array.prototype.forEach.call(listFirstInstance.liCollection, (list) => {
firstListData.forEach((data, index) => {
if (list.innerText.trim() === data.text) {
delete firstListData[index];
}
});
});
if (firstInput) {
firstInput.value = "";
}
let ds = [];
firstListData.forEach(data => {
ds.push(data);
});
firstListData = ds;
}
}
function updateSecondListData() {
Array.prototype.forEach.call(listSecondInstance.liCollection, (list) => {
secondListData.forEach((data, index) => {
if (list.innerText.trim() === data.text) {
delete secondListData[index];
}
});
});
if (secondInput) {
secondInput.value = "";
}
let ds = [];
secondListData.forEach(data => {
ds.push(data);
});
secondListData = ds;
}
function onFirstKeyUp(e) {
if (firstInput) {
let value = firstInput.value;
var data = new DataManager(firstListData).executeLocal(new Query().where("text", "startswith", value, true));
if (listFirstInstance) {
if (!value) {
listFirstInstance.dataSource = firstListData.slice();
}
else {
listFirstInstance.dataSource = data;
}
listFirstInstance.dataBind();
}
}
}
function onSecondKeyUp(e) {
if (secondInput) {
let value = secondInput.value;
var data = new DataManager(secondListData).executeLocal(new Query().where("text", "startswith", value, true));
if (listSecondInstance) {
if (!value) {
listSecondInstance.dataSource = secondListData.slice();
}
else {
listSecondInstance.dataSource = data;
}
listSecondInstance.dataBind();
}
}
}
function setButtonState() {
if (listFirstInstance &&
firstBtnInstance &&
secondBtnInstance &&
listSecondInstance &&
fourthBtnInstance &&
thirdBtnInstance) {
if (listFirstInstance.dataSource.length) {
firstBtnInstance.disabled = false;
}
else {
firstBtnInstance.disabled = true;
secondBtnInstance.disabled = true;
}
if (listSecondInstance.dataSource.length) {
fourthBtnInstance.disabled = false;
}
else {
fourthBtnInstance.disabled = true;
thirdBtnInstance.disabled = true;
}
}
}
function firstBtnClick() {
if (listFirstInstance && firstBtnInstance && listSecondInstance) {
listSecondInstance.dataSource = Array.prototype.concat.call(listFirstInstance.dataSource, listSecondInstance.dataSource);
updateFirstListData();
listFirstInstance.removeMultipleItems(listFirstInstance.liCollection);
firstListData = firstListData.concat(listFirstInstance.dataSource);
secondListData = listSecondInstance.dataSource.slice();
firstBtnInstance.disabled = true;
onFirstKeyUp(null);
setButtonState();
}
}
function secondBtnClick() {
if (listFirstInstance && secondBtnInstance && listSecondInstance) {
let e = listFirstInstance.getSelectedItems();
if (e) {
listSecondInstance.dataSource = Array.prototype.concat.call(listSecondInstance.dataSource, e.data);
listFirstInstance.removeItem(e.item);
firstListData = listFirstInstance.dataSource;
secondListData = listSecondInstance.dataSource.slice();
onFirstKeyUp(null);
secondBtnInstance.disabled = true;
setButtonState();
}
}
}
function thirdBtnClick() {
if (listFirstInstance && listSecondInstance && thirdBtnInstance) {
let e = listSecondInstance.getSelectedItems();
if (e) {
listFirstInstance.dataSource = Array.prototype.concat.call(listFirstInstance.dataSource, e.data);
listSecondInstance.removeItem(e.item);
secondListData = listSecondInstance.dataSource;
firstListData = listFirstInstance.dataSource.slice();
onSecondKeyUp(null);
thirdBtnInstance.disabled = true;
setButtonState();
}
}
}
function fourthBtnClick() {
if (listFirstInstance && listSecondInstance && fourthBtnInstance) {
listFirstInstance.dataSource = Array.prototype.concat.call(listFirstInstance.dataSource, listSecondInstance.dataSource);
updateSecondListData();
listSecondInstance.removeMultipleItems(listSecondInstance.liCollection);
secondListData = secondListData.concat(listSecondInstance.dataSource);
firstListData = listFirstInstance.dataSource.slice();
onSecondKeyUp(null);
setButtonState();
}
}
function firstInputKeyUp() {
if (listFirstInstance &&
firstBtnInstance &&
secondBtnInstance &&
listSecondInstance &&
fourthBtnInstance &&
thirdBtnInstance &&
firstInput) {
let value = firstInput.value;
var data = new DataManager(firstListData).executeLocal(new Query().where("text", "startswith", value, true));
if (!value) {
listFirstInstance.dataSource = firstListData.slice();
}
else {
listFirstInstance.dataSource = data;
}
listFirstInstance.dataBind();
}
}
function secondInputKeyUp() {
if (listFirstInstance &&
firstBtnInstance &&
secondBtnInstance &&
listSecondInstance &&
fourthBtnInstance &&
thirdBtnInstance &&
secondInput) {
let value = secondInput.value;
var data = new DataManager(secondListData).executeLocal(new Query().where("text", "startswith", value, true));
if (!value) {
listSecondInstance.dataSource = secondListData.slice();
}
else {
listSecondInstance.dataSource = data;
}
listSecondInstance.dataBind();
}
}
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) => (firstInput = inputRef)} placeholder="Filter" onKeyUp={firstInputKeyUp.bind(this)} title="Type in a name" />
</div>
<ListViewComponent id="list-1" dataSource={firstListData} fields={fields} sortOrder="Ascending" select={onFirstListSelect.bind(this)} ref={scope => {
listFirstInstance = scope;
}} />
<div id="btn">
<ButtonComponent onClick={firstBtnClick.bind(this)} ref={button1 => {
firstBtnInstance = button1;
}}>
{" "}
{">>"}
</ButtonComponent>
<ButtonComponent onClick={secondBtnClick.bind(this)} ref={button2 => {
secondBtnInstance = button2;
}}>
{" "}
{">"}
</ButtonComponent>
<ButtonComponent onClick={thirdBtnClick.bind(this)} ref={button3 => {
thirdBtnInstance = button3;
}}>
{" "}
{"<"}{" "}
</ButtonComponent>
<ButtonComponent onClick={fourthBtnClick.bind(this)} ref={button4 => {
fourthBtnInstance = button4;
}}>
{" "}
{"<<"}{" "}
</ButtonComponent>
</div>
<div id="text2">
<input className="e-input" type="text" onKeyUp={secondInputKeyUp.bind(this)} id="secondInput" ref={(inputRef) => (secondInput = inputRef)} placeholder=" Filter" title="Type in a name" />
</div>
<ListViewComponent id="list-2" dataSource={secondListData} fields={fields} sortOrder="Ascending" select={onSecondListSelect.bind(this)} ref={list => {
listSecondInstance = list;
}} />
</div>
</div>
</div>);
}
export default App;
ReactDOM.render(<App />, document.getElementById('element'));
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);
function App() {
React.useEffect(() => {
componentDidMount();
}, []);
//Define an array of JSON data
let 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" }
];
let 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" }
];
let firstInput: HTMLInputElement | null = null;
let secondInput: HTMLInputElement | null = null;
let listFirstInstance: ListViewComponent | null = null;
let listSecondInstance: ListViewComponent | null = null;
let firstBtnInstance: ButtonComponent | null = null;
let secondBtnInstance: ButtonComponent | null = null;
let thirdBtnInstance: ButtonComponent | null = null;
let fourthBtnInstance: ButtonComponent | null = null;
function componentDidMount() {
if (listFirstInstance && listSecondInstance) {
firstListData = (listFirstInstance.dataSource as any[]).slice();
secondListData = (listSecondInstance.dataSource as any[]).slice();
}
}
//Map the appropriate columns to fields property
let fields: Object = { text: "text", id: "id" };
function onFirstListSelect() {
if (secondBtnInstance) secondBtnInstance.disabled = false;
}
function onSecondListSelect() {
if (thirdBtnInstance) thirdBtnInstance.disabled = false;
}
function updateFirstListData() {
if (listFirstInstance) {
Array.prototype.forEach.call(
(listFirstInstance as any).liCollection as any[],
(list: any) => {
firstListData.forEach((data, index) => {
if (list.innerText.trim() === data.text) {
delete firstListData[index];
}
});
}
);
if (firstInput) {
firstInput.value = "";
}
let ds: any[] = [];
firstListData.forEach(data => {
ds.push(data);
});
firstListData = ds;
}
}
function updateSecondListData() {
Array.prototype.forEach.call((listSecondInstance as any).liCollection, (list: any) => {
secondListData.forEach((data, index) => {
if (list.innerText.trim() === data.text) {
delete secondListData[index];
}
});
});
if (secondInput) {
secondInput.value = "";
}
let ds: any[] = [];
secondListData.forEach(data => {
ds.push(data);
});
secondListData = ds;
}
function onFirstKeyUp(e: any) {
if (firstInput) {
let value = firstInput.value;
var data = new DataManager(firstListData).executeLocal(
new Query().where("text", "startswith", value, true)
);
if (listFirstInstance) {
if (!value) {
listFirstInstance.dataSource = firstListData.slice();
} else {
listFirstInstance.dataSource = data as any[];
}
listFirstInstance.dataBind();
}
}
}
function onSecondKeyUp(e: any) {
if (secondInput) {
let value = secondInput.value;
var data = new DataManager(secondListData).executeLocal(
new Query().where("text", "startswith", value, true)
);
if (listSecondInstance) {
if (!value) {
listSecondInstance.dataSource = secondListData.slice();
} else {
listSecondInstance.dataSource = data as any[];
}
listSecondInstance.dataBind();
}
}
}
function setButtonState() {
if (
listFirstInstance &&
firstBtnInstance &&
secondBtnInstance &&
listSecondInstance &&
fourthBtnInstance &&
thirdBtnInstance
) {
if ((listFirstInstance.dataSource as any[]).length) {
firstBtnInstance.disabled = false;
} else {
firstBtnInstance.disabled = true;
secondBtnInstance.disabled = true;
}
if ((listSecondInstance.dataSource as any[]).length) {
fourthBtnInstance.disabled = false;
} else {
fourthBtnInstance.disabled = true;
thirdBtnInstance.disabled = true;
}
}
}
function firstBtnClick() {
if (listFirstInstance && firstBtnInstance && listSecondInstance) {
listSecondInstance.dataSource = Array.prototype.concat.call(
listFirstInstance.dataSource,
listSecondInstance.dataSource
);
updateFirstListData();
listFirstInstance.removeMultipleItems((listFirstInstance as any).liCollection);
firstListData = firstListData.concat(listFirstInstance.dataSource);
secondListData = listSecondInstance.dataSource.slice();
firstBtnInstance.disabled = true;
onFirstKeyUp(null);
setButtonState();
}
}
function secondBtnClick() {
if (listFirstInstance && secondBtnInstance && listSecondInstance) {
let e = listFirstInstance.getSelectedItems();
if (e) {
listSecondInstance.dataSource = Array.prototype.concat.call(
listSecondInstance.dataSource,
e.data
);
listFirstInstance.removeItem(e.item as any);
firstListData = listFirstInstance.dataSource as any[];
secondListData = listSecondInstance.dataSource.slice();
onFirstKeyUp(null);
secondBtnInstance.disabled = true;
setButtonState();
}
}
}
function thirdBtnClick() {
if (listFirstInstance && listSecondInstance && thirdBtnInstance) {
let e = listSecondInstance.getSelectedItems();
if (e) {
listFirstInstance.dataSource = Array.prototype.concat.call(
listFirstInstance.dataSource,
e.data
);
listSecondInstance.removeItem(e.item as any);
secondListData = listSecondInstance.dataSource as any[];
firstListData = listFirstInstance.dataSource.slice();
onSecondKeyUp(null);
thirdBtnInstance.disabled = true;
setButtonState();
}
}
}
function fourthBtnClick() {
if (listFirstInstance && listSecondInstance && fourthBtnInstance) {
listFirstInstance.dataSource = Array.prototype.concat.call(
listFirstInstance.dataSource,
listSecondInstance.dataSource
);
updateSecondListData();
listSecondInstance.removeMultipleItems((listSecondInstance as any).liCollection);
secondListData = secondListData.concat(listSecondInstance.dataSource);
firstListData = listFirstInstance.dataSource.slice();
onSecondKeyUp(null);
setButtonState();
}
}
function firstInputKeyUp() {
if (
listFirstInstance &&
firstBtnInstance &&
secondBtnInstance &&
listSecondInstance &&
fourthBtnInstance &&
thirdBtnInstance &&
firstInput
) {
let value = firstInput.value;
var data = new DataManager(firstListData).executeLocal(
new Query().where("text", "startswith", value, true)
);
if (!value) {
listFirstInstance.dataSource = firstListData.slice();
} else {
listFirstInstance.dataSource = data as any[];
}
listFirstInstance.dataBind();
}
}
function secondInputKeyUp() {
if (
listFirstInstance &&
firstBtnInstance &&
secondBtnInstance &&
listSecondInstance &&
fourthBtnInstance &&
thirdBtnInstance &&
secondInput
) {
let value = secondInput.value;
var data = new DataManager(secondListData).executeLocal(
new Query().where("text", "startswith", value, true)
);
if (!value) {
listSecondInstance.dataSource = secondListData.slice();
} else {
listSecondInstance.dataSource = data as any[];
}
listSecondInstance.dataBind();
}
}
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) => (firstInput = inputRef)}
placeholder="Filter"
onKeyUp={firstInputKeyUp.bind(this)}
title="Type in a name"
/>
</div>
<ListViewComponent
id="list-1"
dataSource={firstListData}
fields={fields}
sortOrder="Ascending"
select={onFirstListSelect.bind(this)}
ref={scope => {
listFirstInstance = scope;
}}
/>
<div id="btn">
<ButtonComponent
onClick={firstBtnClick.bind(this)}
ref={button1 => {
firstBtnInstance = button1;
}}
>
{" "}
{">>"}
</ButtonComponent>
<ButtonComponent
onClick={secondBtnClick.bind(this)}
ref={button2 => {
secondBtnInstance = button2;
}}
>
{" "}
{">"}
</ButtonComponent>
<ButtonComponent
onClick={thirdBtnClick.bind(this)}
ref={button3 => {
thirdBtnInstance = button3;
}}
>
{" "}
{"<"}{" "}
</ButtonComponent>
<ButtonComponent
onClick={fourthBtnClick.bind(this)}
ref={button4 => {
fourthBtnInstance = button4;
}}
>
{" "}
{"<<"}{" "}
</ButtonComponent>
</div>
<div id="text2">
<input
className="e-input"
type="text"
onKeyUp={secondInputKeyUp.bind(this)}
id="secondInput"
ref={(inputRef: any) => (secondInput = inputRef)}
placeholder=" Filter"
title="Type in a name"
/>
</div>
<ListViewComponent
id="list-2"
dataSource={secondListData}
fields={fields}
sortOrder="Ascending"
select={onSecondListSelect.bind(this)}
ref={list => {
listSecondInstance = list;
}}
/>
</div>
</div>
</div>
);
}
export default App;
ReactDOM.render(<App />, document.getElementById('element'));
#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;
}
<!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="https://cdn.syncfusion.com/ej2/29.1.33/ej2-base/styles/material.css" rel="stylesheet" />
<link href="https://cdn.syncfusion.com/ej2/29.1.33/ej2-react-lists/styles/material.css" rel="stylesheet" />
<link href="https://cdn.syncfusion.com/ej2/29.1.33/ej2-react-buttons/styles/material.css" rel="stylesheet" />
<link href="https://cdn.syncfusion.com/ej2/29.1.33/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>
<script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type ="text/javascript"></script>
</head>
<body>
<div id='element' style="margin:0 auto; max-width:500px;">
<div id='loader'>Loading....</div>
</div>
</body>
</html>