If you have technical questions, please create a support ticket in the DevExpress Support Center.
import React, { useState } from 'react';
import { TagBox } from 'devextreme-react/tag-box';
import DataSource from 'devextreme/data/data_source';
import Group from './Group.tsx';
import productsData from './data.ts';
const defaultValues = {
grouped: [17, 19],
search: [17, 19],
template: [18],
};
const productLabel = { 'aria-label': 'Product' };
function App() {
const [products] = useState(
new DataSource({
store: productsData,
key: 'ID',
group: 'Category',
}),
);
return (
<div className="dx-fieldset">
<div className="dx-field">
<div className="dx-field-label">Grouped items</div>
<div className="dx-field-value">
<TagBox
dataSource={products}
inputAttr={productLabel}
valueExpr="ID"
defaultValue={defaultValues.grouped}
grouped={true}
displayExpr="Name"
/>
</div>
</div>
<div className="dx-field">
<div className="dx-field-label">Grouped items with search enabled</div>
<div className="dx-field-value">
<TagBox
dataSource={products}
valueExpr="ID"
inputAttr={productLabel}
defaultValue={defaultValues.search}
searchEnabled={true}
grouped={true}
displayExpr="Name"
/>
</div>
</div>
<div className="dx-field">
<div className="dx-field-label">Grouped items with a custom group template</div>
<div className="dx-field-value">
<TagBox
dataSource={products}
valueExpr="ID"
inputAttr={productLabel}
defaultValue={defaultValues.template}
grouped={true}
displayExpr="Name"
groupRender={Group}
/>
</div>
</div>
</div>
);
}
export default App;
xxxxxxxxxx
import React, { useState } from 'react';
import { TagBox } from 'devextreme-react/tag-box';
import DataSource from 'devextreme/data/data_source';
import Group from './Group.js';
import productsData from './data.js';
const defaultValues = {
grouped: [17, 19],
search: [17, 19],
template: [18],
};
const productLabel = { 'aria-label': 'Product' };
function App() {
const [products] = useState(
new DataSource({
store: productsData,
key: 'ID',
group: 'Category',
}),
);
return (
<div className="dx-fieldset">
<div className="dx-field">
<div className="dx-field-label">Grouped items</div>
<div className="dx-field-value">
<TagBox
dataSource={products}
inputAttr={productLabel}
valueExpr="ID"
defaultValue={defaultValues.grouped}
grouped={true}
displayExpr="Name"
/>
</div>
</div>
<div className="dx-field">
<div className="dx-field-label">Grouped items with search enabled</div>
<div className="dx-field-value">
<TagBox
dataSource={products}
valueExpr="ID"
inputAttr={productLabel}
defaultValue={defaultValues.search}
searchEnabled={true}
grouped={true}
displayExpr="Name"
/>
</div>
</div>
<div className="dx-field">
<div className="dx-field-label">Grouped items with a custom group template</div>
<div className="dx-field-value">
<TagBox
dataSource={products}
valueExpr="ID"
inputAttr={productLabel}
defaultValue={defaultValues.template}
grouped={true}
displayExpr="Name"
groupRender={Group}
/>
</div>
</div>
</div>
);
}
export default App;
xxxxxxxxxx
import React from 'react';
export default function Group({ key }) {
return (
<div className="custom-icon">
<span className="dx-icon-box icon"></span> {key}
</div>
);
}
xxxxxxxxxx
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App.tsx';
ReactDOM.render(<App />, document.getElementById('app'));
xxxxxxxxxx
export default [{
ID: 1,
Name: 'HD Video Player',
Category: 'Video Players',
}, {
ID: 2,
Name: 'SuperHD Player',
Category: 'Video Players',
}, {
ID: 3,
Name: 'SuperPlasma 50',
Category: 'Televisions',
}, {
ID: 4,
Name: 'SuperLED 50',
Category: 'Televisions',
}, {
ID: 5,
Name: 'SuperLED 42',
Category: 'Televisions',
}, {
ID: 6,
Name: 'SuperLCD 55',
Category: 'Televisions',
}, {
ID: 7,
Name: 'SuperLCD 42',
Category: 'Televisions',
}, {
ID: 8,
Name: 'SuperPlasma 65',
Category: 'Televisions',
}, {
ID: 9,
Name: 'SuperLCD 70',
Category: 'Televisions',
}, {
ID: 10,
Name: 'DesktopLED 21',
Category: 'Monitors',
}, {
ID: 11,
Name: 'DesktopLED 19',
Category: 'Monitors',
}, {
ID: 12,
Name: 'DesktopLCD 21',
Category: 'Monitors',
}, {
ID: 13,
Name: 'DesktopLCD 19',
Category: 'Monitors',
}, {
ID: 14,
Name: 'Projector Plus',
Category: 'Projectors',
}, {
ID: 15,
Name: 'Projector PlusHD',
Category: 'Projectors',
}, {
ID: 16,
Name: 'Projector PlusHT',
Category: 'Projectors',
}, {
ID: 17,
Name: 'ExcelRemote IR',
Category: 'Automation',
}, {
ID: 18,
Name: 'ExcelRemote BT',
Category: 'Automation',
}, {
ID: 19,
Name: 'ExcelRemote IP',
Category: 'Automation',
}];
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';
export default function Group({ key }) {
return (
<div className="custom-icon">
<span className="dx-icon-box icon"></span> {key}
</div>
);
}
xxxxxxxxxx
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App.js';
ReactDOM.render(<App />, document.getElementById('app'));
xxxxxxxxxx
export default [
{
ID: 1,
Name: 'HD Video Player',
Category: 'Video Players',
},
{
ID: 2,
Name: 'SuperHD Player',
Category: 'Video Players',
},
{
ID: 3,
Name: 'SuperPlasma 50',
Category: 'Televisions',
},
{
ID: 4,
Name: 'SuperLED 50',
Category: 'Televisions',
},
{
ID: 5,
Name: 'SuperLED 42',
Category: 'Televisions',
},
{
ID: 6,
Name: 'SuperLCD 55',
Category: 'Televisions',
},
{
ID: 7,
Name: 'SuperLCD 42',
Category: 'Televisions',
},
{
ID: 8,
Name: 'SuperPlasma 65',
Category: 'Televisions',
},
{
ID: 9,
Name: 'SuperLCD 70',
Category: 'Televisions',
},
{
ID: 10,
Name: 'DesktopLED 21',
Category: 'Monitors',
},
{
ID: 11,
Name: 'DesktopLED 19',
Category: 'Monitors',
},
{
ID: 12,
Name: 'DesktopLCD 21',
Category: 'Monitors',
},
{
ID: 13,
Name: 'DesktopLCD 19',
Category: 'Monitors',
},
{
ID: 14,
Name: 'Projector Plus',
Category: 'Projectors',
},
{
ID: 15,
Name: 'Projector PlusHD',
Category: 'Projectors',
},
{
ID: 16,
Name: 'Projector PlusHT',
Category: 'Projectors',
},
{
ID: 17,
Name: 'ExcelRemote IR',
Category: 'Automation',
},
{
ID: 18,
Name: 'ExcelRemote BT',
Category: 'Automation',
},
{
ID: 19,
Name: 'ExcelRemote IP',
Category: 'Automation',
},
];
xxxxxxxxxx
<html lang="en">
<head></head>
<body class="dx-viewport">
<div class="demo-container">
<div id="app"></div>
</div>
</body>
</html>
xxxxxxxxxx
.custom-icon .icon {
color: #f05b41;
}
.custom-icon {
display: flex;
align-items: center;
gap: 4px;
}
.dx-field {
margin-bottom: 50px;
}
Ensure the data source defines the hierarchy in one of the following ways:
-
Grouping Field
The data source can contain a plain array of objects with multiple fields. Assign a field to the DataSource's group property to create a hierarchical display. Parent items display unique values from the specified field, and Child items display grouped values. This demo shows how to use this data source type. -
Hierarchical Data Source Structure
You can use a dataSource where each entry is an object that contains the key and items fields. The key field specifies the group header (parent), and the items field holds an array of child items. A data source can specify different field names if the data is structured in the same way. In this case, implement the DataSource's map function to create key and items field mappings. Refer to the following help topic for more information: Item Mapping.
This demo also shows how to customize the appearance of group headers (the groupTemplate property) and enable search (the searchEnabled property).