If you have technical questions, please create a support ticket in the DevExpress Support Center.
import React, { useCallback, useRef, useState } from 'react';
import TreeView, { TreeViewTypes } from 'devextreme-react/tree-view';
import List from 'devextreme-react/list';
import SelectBox, { SelectBoxTypes } from 'devextreme-react/select-box';
import CheckBox, { CheckBoxTypes } from 'devextreme-react/check-box';
import { employees, showCheckboxesModeLabel, selectionModeLabel } from './data.ts';
const showCheckBoxesModes: TreeViewTypes.TreeViewCheckBoxMode[] = ['normal', 'selectAll', 'none'];
const selectionModes: TreeViewTypes.SingleOrMultiple[] = ['multiple', 'single'];
const renderTreeViewItem = (item: { fullName: any; position: any; }) => `${item.fullName} (${item.position})`;
const renderListItem = (item: { prefix: any; fullName: any; position: any; }) => `${item.prefix} ${item.fullName} (${item.position})`;
const App = () => {
const treeViewRef = useRef(null);
const [selectedEmployees, setSelectedEmployees] = useState([]);
const [selectNodesRecursive, setSelectNodesRecursive] = useState(true);
const [selectByClick, setSelectByClick] = useState(false);
const [showCheckBoxesMode, setShowCheckBoxesMode] = useState(showCheckBoxesModes[0]);
const [selectionMode, setSelectionMode] = useState(selectionModes[0]);
const [isSelectionModeDisabled, setIsSelectionModeDisabled] = useState(false);
const [isRecursiveDisabled, setIsRecursiveDisabled] = useState(false);
const syncSelection = useCallback((treeView) => {
const syncSelectedEmployees = treeView.getSelectedNodes()
.map((node) => node.itemData);
setSelectedEmployees(syncSelectedEmployees);
}, [setSelectedEmployees]);
const treeViewSelectionChanged = useCallback((e: TreeViewTypes.SelectionChangedEvent) => {
syncSelection(e.component);
}, [syncSelection]);
const treeViewContentReady = useCallback((e: TreeViewTypes.ContentReadyEvent) => {
syncSelection(e.component);
}, [syncSelection]);
const showCheckBoxesModeValueChanged = useCallback((e: SelectBoxTypes.ValueChangedEvent) => {
const value = e.value;
setShowCheckBoxesMode(value);
if (value === 'selectAll') {
setSelectionMode('multiple');
setIsRecursiveDisabled(false);
}
setIsSelectionModeDisabled(value === 'selectAll');
}, [setShowCheckBoxesMode, setSelectionMode, setIsRecursiveDisabled, setIsSelectionModeDisabled]);
const selectionModeValueChanged = useCallback((e: SelectBoxTypes.ValueChangedEvent) => {
const value = e.value;
setSelectionMode(value);
if (value === 'single') {
setSelectNodesRecursive(false);
treeViewRef.current.instance().unselectAll();
}
setIsRecursiveDisabled(value === 'single');
}, [setSelectionMode, setSelectNodesRecursive, setIsRecursiveDisabled]);
const selectNodesRecursiveValueChanged = useCallback((e: CheckBoxTypes.ValueChangedEvent) => {
setSelectNodesRecursive(e.value);
}, [setSelectNodesRecursive]);
const selectByClickValueChanged = useCallback((e: CheckBoxTypes.ValueChangedEvent) => {
setSelectByClick(e.value);
}, [setSelectByClick]);
return (
<div>
<div className="form">
<h4>Employees</h4>
<TreeView
id="treeview"
ref={treeViewRef}
width={340}
height={320}
items={employees}
selectNodesRecursive={selectNodesRecursive}
selectByClick={selectByClick}
showCheckBoxesMode={showCheckBoxesMode}
selectionMode={selectionMode}
onSelectionChanged={treeViewSelectionChanged}
onContentReady={treeViewContentReady}
itemRender={renderTreeViewItem}
/>
{' '}
<div className="selected-container">Selected employees
<List
id="selected-employees"
width={400}
height={200}
showScrollbar="always"
items={selectedEmployees}
itemRender={renderListItem}
/>
</div>
</div>
<div className="options">
<div className="caption">Options</div>
<div className="options-container">
<div className="option">
<span>Show Check Boxes Mode:</span>
<div className="editor-container">
<SelectBox
items={showCheckBoxesModes}
value={showCheckBoxesMode}
inputAttr={showCheckboxesModeLabel}
onValueChanged={showCheckBoxesModeValueChanged} />
</div>
</div>
<div className="option">
<span>Selection Mode:</span>
<div className="editor-container">
<SelectBox
items={selectionModes}
value={selectionMode}
inputAttr={selectionModeLabel}
disabled={isSelectionModeDisabled}
onValueChanged={selectionModeValueChanged} />
</div>
</div>
<div className="option">
<div className="caption-placeholder"> </div>
<div className="editor-container">
<CheckBox
text="Select Nodes Recursive"
value={selectNodesRecursive}
disabled={isRecursiveDisabled}
onValueChanged={selectNodesRecursiveValueChanged} />
</div>
</div>
<div className="option">
<div className="caption-placeholder"> </div>
<div className="editor-container">
<CheckBox
text="Select By Click"
value={selectByClick}
onValueChanged={selectByClickValueChanged} />
</div>
</div>
</div>
</div>
</div>
);
};
export default App;
xxxxxxxxxx
import React, { useCallback, useRef, useState } from 'react';
import TreeView from 'devextreme-react/tree-view';
import List from 'devextreme-react/list';
import SelectBox from 'devextreme-react/select-box';
import CheckBox from 'devextreme-react/check-box';
import { employees, showCheckboxesModeLabel, selectionModeLabel } from './data.js';
const showCheckBoxesModes = ['normal', 'selectAll', 'none'];
const selectionModes = ['multiple', 'single'];
const renderTreeViewItem = (item) => `${item.fullName} (${item.position})`;
const renderListItem = (item) => `${item.prefix} ${item.fullName} (${item.position})`;
const App = () => {
const treeViewRef = useRef(null);
const [selectedEmployees, setSelectedEmployees] = useState([]);
const [selectNodesRecursive, setSelectNodesRecursive] = useState(true);
const [selectByClick, setSelectByClick] = useState(false);
const [showCheckBoxesMode, setShowCheckBoxesMode] = useState(showCheckBoxesModes[0]);
const [selectionMode, setSelectionMode] = useState(selectionModes[0]);
const [isSelectionModeDisabled, setIsSelectionModeDisabled] = useState(false);
const [isRecursiveDisabled, setIsRecursiveDisabled] = useState(false);
const syncSelection = useCallback(
(treeView) => {
const syncSelectedEmployees = treeView.getSelectedNodes().map((node) => node.itemData);
setSelectedEmployees(syncSelectedEmployees);
},
[setSelectedEmployees],
);
const treeViewSelectionChanged = useCallback(
(e) => {
syncSelection(e.component);
},
[syncSelection],
);
const treeViewContentReady = useCallback(
(e) => {
syncSelection(e.component);
},
[syncSelection],
);
const showCheckBoxesModeValueChanged = useCallback(
(e) => {
const value = e.value;
setShowCheckBoxesMode(value);
if (value === 'selectAll') {
setSelectionMode('multiple');
setIsRecursiveDisabled(false);
}
setIsSelectionModeDisabled(value === 'selectAll');
},
[setShowCheckBoxesMode, setSelectionMode, setIsRecursiveDisabled, setIsSelectionModeDisabled],
);
const selectionModeValueChanged = useCallback(
(e) => {
const value = e.value;
setSelectionMode(value);
if (value === 'single') {
setSelectNodesRecursive(false);
treeViewRef.current.instance().unselectAll();
}
setIsRecursiveDisabled(value === 'single');
},
[setSelectionMode, setSelectNodesRecursive, setIsRecursiveDisabled],
);
const selectNodesRecursiveValueChanged = useCallback(
(e) => {
setSelectNodesRecursive(e.value);
},
[setSelectNodesRecursive],
);
const selectByClickValueChanged = useCallback(
(e) => {
setSelectByClick(e.value);
},
[setSelectByClick],
);
return (
<div>
<div className="form">
<h4>Employees</h4>
<TreeView
id="treeview"
ref={treeViewRef}
width={340}
height={320}
items={employees}
selectNodesRecursive={selectNodesRecursive}
selectByClick={selectByClick}
showCheckBoxesMode={showCheckBoxesMode}
selectionMode={selectionMode}
onSelectionChanged={treeViewSelectionChanged}
onContentReady={treeViewContentReady}
itemRender={renderTreeViewItem}
/>{' '}
<div className="selected-container">
Selected employees
<List
id="selected-employees"
width={400}
height={200}
showScrollbar="always"
items={selectedEmployees}
itemRender={renderListItem}
/>
</div>
</div>
<div className="options">
<div className="caption">Options</div>
<div className="options-container">
<div className="option">
<span>Show Check Boxes Mode:</span>
<div className="editor-container">
<SelectBox
items={showCheckBoxesModes}
value={showCheckBoxesMode}
inputAttr={showCheckboxesModeLabel}
onValueChanged={showCheckBoxesModeValueChanged}
/>
</div>
</div>
<div className="option">
<span>Selection Mode:</span>
<div className="editor-container">
<SelectBox
items={selectionModes}
value={selectionMode}
inputAttr={selectionModeLabel}
disabled={isSelectionModeDisabled}
onValueChanged={selectionModeValueChanged}
/>
</div>
</div>
<div className="option">
<div className="caption-placeholder"> </div>
<div className="editor-container">
<CheckBox
text="Select Nodes Recursive"
value={selectNodesRecursive}
disabled={isRecursiveDisabled}
onValueChanged={selectNodesRecursiveValueChanged}
/>
</div>
</div>
<div className="option">
<div className="caption-placeholder"> </div>
<div className="editor-container">
<CheckBox
text="Select By Click"
value={selectByClick}
onValueChanged={selectByClickValueChanged}
/>
</div>
</div>
</div>
</div>
</div>
);
};
export default App;
xxxxxxxxxx
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App.tsx';
ReactDOM.render(
<App />,
document.getElementById('app'),
);
xxxxxxxxxx
export const employees = [{
id: 1,
fullName: 'John Heart',
prefix: 'Dr.',
position: 'CEO',
expanded: true,
items: [{
id: 2,
fullName: 'Samantha Bright',
prefix: 'Dr.',
position: 'COO',
expanded: true,
items: [{
id: 3,
fullName: 'Kevin Carter',
prefix: 'Mr.',
position: 'Shipping Manager',
}, {
id: 14,
fullName: 'Victor Norris',
prefix: 'Mr.',
selected: true,
position: 'Shipping Assistant',
}],
}, {
id: 4,
fullName: 'Brett Wade',
prefix: 'Mr.',
position: 'IT Manager',
expanded: true,
items: [{
id: 5,
fullName: 'Amelia Harper',
prefix: 'Mrs.',
position: 'Network Admin',
}, {
id: 6,
fullName: 'Wally Hobbs',
prefix: 'Mr.',
position: 'Programmer',
}, {
id: 7,
fullName: 'Brad Jameson',
prefix: 'Mr.',
position: 'Programmer',
}, {
id: 8,
fullName: 'Violet Bailey',
prefix: 'Ms.',
position: 'Jr Graphic Designer',
}],
}, {
id: 9,
fullName: 'Barb Banks',
prefix: 'Mrs.',
position: 'Support Manager',
expanded: true,
items: [{
id: 10,
fullName: 'Kelly Rodriguez',
prefix: 'Ms.',
position: 'Support Assistant',
}, {
id: 11,
fullName: 'James Anderson',
prefix: 'Mr.',
position: 'Support Assistant',
}],
}],
}];
export const selectionModeLabel = { 'aria-label': 'Selection Mode' };
export const showCheckboxesModeLabel = { 'aria-label': 'Show Checkboxes Mode' };
xxxxxxxxxx
window.exports = window.exports || {};
window.config = {
transpiler: 'ts',
typescriptOptions: {
module: 'system',
emitDecoratorMetadata: true,
experimentalDecorators: true,
jsx: 'react',
},
meta: {
'react': {
'esModule': true,
},
'typescript': {
'exports': 'ts',
},
'devextreme/time_zone_utils.js': {
'esModule': true,
},
'devextreme/localization.js': {
'esModule': true,
},
'devextreme/viz/palette.js': {
'esModule': true,
},
'openai': {
'esModule': true,
},
},
paths: {
'npm:': 'https://unpkg.com/',
'bundles:': 'bundles/',
'externals:': 'bundles/externals/',
},
defaultExtension: 'js',
map: {
'ts': 'npm:plugin-typescript@8.0.0/lib/plugin.js',
'typescript': 'npm:typescript@4.2.4/lib/typescript.js',
'jszip': 'npm:jszip@3.10.1/dist/jszip.min.js',
'react': 'npm:react@17.0.2/umd/react.development.js',
'react-dom': 'npm:react-dom@17.0.2/umd/react-dom.development.js',
'prop-types': 'npm:prop-types/prop-types.js',
'rrule': 'npm:rrule@2.6.4/dist/es5/rrule.js',
'luxon': 'npm:luxon@3.4.4/build/global/luxon.min.js',
'es6-object-assign': 'npm:es6-object-assign',
'devextreme': 'npm:devextreme@link:../../packages/devextreme/artifacts/npm/devextreme/cjs',
'devextreme-react': 'npm:devextreme-react@link:../../packages/devextreme-react/npm/cjs',
'devextreme-quill': 'npm:devextreme-quill@1.7.1/dist/dx-quill.min.js',
'devexpress-diagram': 'npm:devexpress-diagram@2.2.5/dist/dx-diagram.js',
'devexpress-gantt': 'npm:devexpress-gantt@4.1.54/dist/dx-gantt.js',
'@devextreme/runtime': 'npm:@devextreme/runtime@3.0.12',
'inferno': 'npm:inferno@7.4.11/dist/inferno.min.js',
'inferno-compat': 'npm:inferno-compat/dist/inferno-compat.min.js',
'inferno-create-element': 'npm:inferno-create-element@7.4.11/dist/inferno-create-element.min.js',
'inferno-dom': 'npm:inferno-dom/dist/inferno-dom.min.js',
'inferno-hydrate': 'npm:inferno-hydrate/dist/inferno-hydrate.min.js',
'inferno-clone-vnode': 'npm:inferno-clone-vnode/dist/inferno-clone-vnode.min.js',
'inferno-create-class': 'npm:inferno-create-class/dist/inferno-create-class.min.js',
'inferno-extras': 'npm:inferno-extras/dist/inferno-extras.min.js',
'devextreme-cldr-data': 'npm:devextreme-cldr-data@1.0.3',
// SystemJS plugins
'plugin-babel': 'npm:systemjs-plugin-babel@0.0.25/plugin-babel.js',
'systemjs-babel-build': 'npm:systemjs-plugin-babel@0.0.25/systemjs-babel-browser.js',
// Prettier
'prettier/standalone': 'npm:prettier@2.8.8/standalone.js',
'prettier/parser-html': 'npm:prettier@2.8.8/parser-html.js',
},
packages: {
'devextreme': {
defaultExtension: 'js',
},
'devextreme-react': {
main: 'index.js',
},
'devextreme/events/utils': {
main: 'index',
},
'devextreme/localization/messages': {
format: 'json',
defaultExtension: 'json',
},
'devextreme/events': {
main: 'index',
},
'es6-object-assign': {
main: './index.js',
defaultExtension: 'js',
},
},
packageConfigPaths: [
'npm:@devextreme/*/package.json',
'npm:@devextreme/runtime@3.0.12/inferno/package.json',
],
babelOptions: {
sourceMaps: false,
stage0: true,
react: true,
},
};
System.config(window.config);
xxxxxxxxxx
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App.js';
ReactDOM.render(<App />, document.getElementById('app'));
xxxxxxxxxx
export const employees = [
{
id: 1,
fullName: 'John Heart',
prefix: 'Dr.',
position: 'CEO',
expanded: true,
items: [
{
id: 2,
fullName: 'Samantha Bright',
prefix: 'Dr.',
position: 'COO',
expanded: true,
items: [
{
id: 3,
fullName: 'Kevin Carter',
prefix: 'Mr.',
position: 'Shipping Manager',
},
{
id: 14,
fullName: 'Victor Norris',
prefix: 'Mr.',
selected: true,
position: 'Shipping Assistant',
},
],
},
{
id: 4,
fullName: 'Brett Wade',
prefix: 'Mr.',
position: 'IT Manager',
expanded: true,
items: [
{
id: 5,
fullName: 'Amelia Harper',
prefix: 'Mrs.',
position: 'Network Admin',
},
{
id: 6,
fullName: 'Wally Hobbs',
prefix: 'Mr.',
position: 'Programmer',
},
{
id: 7,
fullName: 'Brad Jameson',
prefix: 'Mr.',
position: 'Programmer',
},
{
id: 8,
fullName: 'Violet Bailey',
prefix: 'Ms.',
position: 'Jr Graphic Designer',
},
],
},
{
id: 9,
fullName: 'Barb Banks',
prefix: 'Mrs.',
position: 'Support Manager',
expanded: true,
items: [
{
id: 10,
fullName: 'Kelly Rodriguez',
prefix: 'Ms.',
position: 'Support Assistant',
},
{
id: 11,
fullName: 'James Anderson',
prefix: 'Mr.',
position: 'Support Assistant',
},
],
},
],
},
];
export const selectionModeLabel = { 'aria-label': 'Selection Mode' };
export const showCheckboxesModeLabel = { 'aria-label': 'Show Checkboxes Mode' };
xxxxxxxxxx
<html lang="en">
<head></head>
<body class="dx-viewport">
<div class="demo-container">
<div id="app"></div>
</div>
</body>
</html>
xxxxxxxxxx
.form > h4 {
margin-bottom: 20px;
}
.form > div,
#treeview {
display: inline-block;
vertical-align: top;
}
#selected-employees {
margin-top: 20px;
}
.selected-container .dx-list-item-content {
padding-left: 0;
}
.selected-container {
padding: 20px;
margin-left: 20px;
background-color: rgba(191, 191, 191, 0.15);
font-size: 115%;
font-weight: bold;
}
.options {
padding: 20px;
background-color: rgba(191, 191, 191, 0.15);
margin-top: 20px;
box-sizing: border-box;
}
.caption {
font-size: 18px;
font-weight: 500;
}
.option {
width: 24%;
margin-top: 10px;
margin-right: 9px;
box-sizing: border-box;
display: flex;
flex-direction: column;
justify-content: center;
}
.options-container {
display: flex;
justify-content: space-between;
align-items: stretch;
}
.editor-container {
height: 100%;
display: flex;
align-items: center;
}
.editor-container > * {
width: 100%;
}
.option:last-of-type {
margin-right: 0;
}
Use the following TreeView properties to adjust selection:
-
selectionMode
Specifies whether multiple node selection is allowed. -
selectNodesRecursive
Specifies whether nested nodes are selected together with their parent. -
selectedExpr
A data field that allows you to pre-select a node. In this demo, the data field is called selected, and it is set to true for the "Victor Norris" node (see the data source). -
onSelectionChanged
A function that allows you to handle selection changes. In this demo, it is used to synchronize the List with the TreeView.
The TreeView also provides the following methods to manage selection programmatically:
-
selectItem / unselectItem
Selects or unselects a single node. Accepts the node key, data object, or DOM node. -
selectAll() / unselectAll()
Selects or unselects all nodes. -
getSelectedNodes() / getSelectedNodeKeys()
Gets the selected nodes or their keys. In this demo, getSelectedNodes() is used to prepare data for the List.
This demo also shows how to specify an itemTemplate for node customization. Node data is passed to the template as an argument.