DevExtreme v23.2 is now available.

Explore our newest features/capabilities and share your thoughts with us.

Your search did not match any results.

Grouped Items

Data items in the SelectBox's drop-down list can be organized in groups.

If the data source contains ungrouped data items, use the DataSource's group property to specify the data field to group by. This case is illustrated in this demo's first and third SelectBoxes.

The SelectBox can also work with initially grouped data items. In this case, the data array should contain objects with the key and items fields:

let dataSource = [{
    key: "Group 1", // Group caption 
    items: [ // Items in Group 1
        { /* ... */ },
        { /* ... */ }
    ]
}, {
    key: "Group 2",
    items: [
        { /* ... */ },
        { /* ... */ }
    ]
}];

If data objects are grouped but use other field names, implement the DataSource's map function to create key and items field mappings as in this demo's second SelectBox.

NOTE

Only one-level grouping is supported.

Regardless of the data source structure, enable the grouped property.

If the data source contains objects, specify the following SelectBox properties:

  • valueExpr
    A data field that contains unique values used to identify items.

  • displayExpr
    A data field whose values should be displayed in the drop-down list.

If you need to specify a custom template for group captions, use the groupTemplate property. In this demo, each caption contains an icon and a text string.

Backend API
import React from 'react'; import { SelectBox } from 'devextreme-react/select-box'; import DataSource from 'devextreme/data/data_source'; import Group from './Group.tsx'; import { ungroupedData, pregroupedData, ungroupedDataLabel, pregroupedDataLabel, templatedUngroupedDataLabel, } from './data.ts'; const fromUngroupedData = new DataSource({ store: { type: 'array', data: ungroupedData, key: 'ID', }, group: 'Category', }); const fromPregroupedData = new DataSource({ store: { type: 'array', data: pregroupedData, key: 'ID', }, map(item) { item.key = item.Category; item.items = item.Products; return item; }, }); fromPregroupedData.load(); function App() { return ( <div className="dx-fieldset"> <div className="dx-field"> <div className="dx-field-label">Data grouped in the DataSource</div> <div className="dx-field-value"> <SelectBox dataSource={fromUngroupedData} valueExpr="ID" grouped={true} inputAttr={ungroupedDataLabel} displayExpr="Name" defaultValue={1} /> </div> </div> <div className="dx-field"> <div className="dx-field-label">Pre-grouped data</div> <div className="dx-field-value"> <SelectBox dataSource={fromPregroupedData} valueExpr="ID" inputAttr={pregroupedDataLabel} grouped={true} displayExpr="Name" defaultValue={1} /> </div> </div> <div className="dx-field"> <div className="dx-field-label">Custom group template</div> <div className="dx-field-value"> <SelectBox dataSource={fromUngroupedData} valueExpr="ID" inputAttr={templatedUngroupedDataLabel} grouped={true} displayExpr="Name" groupRender={Group} defaultValue={1} /> </div> </div> </div> ); } export default App;
import React from 'react'; import { SelectBox } from 'devextreme-react/select-box'; import DataSource from 'devextreme/data/data_source'; import Group from './Group.js'; import { ungroupedData, pregroupedData, ungroupedDataLabel, pregroupedDataLabel, templatedUngroupedDataLabel, } from './data.js'; const fromUngroupedData = new DataSource({ store: { type: 'array', data: ungroupedData, key: 'ID', }, group: 'Category', }); const fromPregroupedData = new DataSource({ store: { type: 'array', data: pregroupedData, key: 'ID', }, map(item) { item.key = item.Category; item.items = item.Products; return item; }, }); fromPregroupedData.load(); function App() { return ( <div className="dx-fieldset"> <div className="dx-field"> <div className="dx-field-label">Data grouped in the DataSource</div> <div className="dx-field-value"> <SelectBox dataSource={fromUngroupedData} valueExpr="ID" grouped={true} inputAttr={ungroupedDataLabel} displayExpr="Name" defaultValue={1} /> </div> </div> <div className="dx-field"> <div className="dx-field-label">Pre-grouped data</div> <div className="dx-field-value"> <SelectBox dataSource={fromPregroupedData} valueExpr="ID" inputAttr={pregroupedDataLabel} grouped={true} displayExpr="Name" defaultValue={1} /> </div> </div> <div className="dx-field"> <div className="dx-field-label">Custom group template</div> <div className="dx-field-value"> <SelectBox dataSource={fromUngroupedData} valueExpr="ID" inputAttr={templatedUngroupedDataLabel} grouped={true} displayExpr="Name" groupRender={Group} defaultValue={1} /> </div> </div> </div> ); } export default App;
import React from 'react'; export default function Group({ key }) { return ( <div className="custom-icon"> <span className="dx-icon-box icon"></span> {key} </div> ); }
import React from 'react'; import ReactDOM from 'react-dom'; import App from './App.tsx'; ReactDOM.render(<App />, document.getElementById('app'));
export const ungroupedData = [{ 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', }]; export const pregroupedData = [{ Category: 'Video Players', Products: [{ ID: 1, Name: 'HD Video Player', }, { ID: 2, Name: 'SuperHD Player', }], }, { Category: 'Televisions', Products: [{ ID: 3, Name: 'SuperPlasma 50', }, { ID: 4, Name: 'SuperLED 50', }, { ID: 5, Name: 'SuperLED 42', }, { ID: 6, Name: 'SuperLCD 55', }, { ID: 7, Name: 'SuperLCD 42', }, { ID: 8, Name: 'SuperPlasma 65', }, { ID: 9, Name: 'SuperLCD 70', }], }, { Category: 'Monitors', Products: [{ ID: 10, Name: 'DesktopLED 21', }, { ID: 11, Name: 'DesktopLED 19', }, { ID: 12, Name: 'DesktopLCD 21', }, { ID: 13, Name: 'DesktopLCD 19', }], }, { Category: 'Projectors', Products: [{ ID: 14, Name: 'Projector Plus', }, { ID: 15, Name: 'Projector PlusHD', }, { ID: 16, Name: 'Projector PlusHT', }], }, { Category: 'Automation', Products: [{ ID: 17, Name: 'ExcelRemote IR', }, { ID: 18, Name: 'ExcelRemote BT', }, { ID: 19, Name: 'ExcelRemote IP', }], }]; export const ungroupedDataLabel = { 'aria-label': 'Ungrouped Data' }; export const pregroupedDataLabel = { 'aria-label': 'Pregrouped Data' }; export const templatedUngroupedDataLabel = { 'aria-label': 'Templated Ungrouped Data' };
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, }, }, paths: { 'npm:': 'https://unpkg.com/', }, defaultExtension: 'js', map: { 'ts': 'npm:plugin-typescript@4.2.4/lib/plugin.js', 'typescript': 'npm:typescript@4.2.4/lib/typescript.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@15.8.1/prop-types.js', 'rrule': 'npm:rrule@2.6.4/dist/es5/rrule.js', 'luxon': 'npm:luxon@1.28.1/build/global/luxon.min.js', 'es6-object-assign': 'npm:es6-object-assign@1.1.0', 'devextreme': 'npm:devextreme@23.2.5/cjs', 'devextreme-react': 'npm:devextreme-react@23.2.5/cjs', 'jszip': 'npm:jszip@3.10.1/dist/jszip.min.js', 'devextreme-quill': 'npm:devextreme-quill@1.6.4/dist/dx-quill.min.js', 'devexpress-diagram': 'npm:devexpress-diagram@2.2.5/dist/dx-diagram.js', 'devexpress-gantt': 'npm:devexpress-gantt@4.1.51/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@7.4.11/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.4/standalone.js', 'prettier/parser-html': 'npm:prettier@2.8.4/parser-html.js', }, packages: { 'devextreme': { defaultExtension: 'js', }, 'devextreme-react': { main: 'index.js', }, 'devextreme/events/utils': { main: 'index', }, 'devextreme/localization/messages': { format: 'json', defaultExtension: '', }, '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);
import React from 'react'; export default function Group({ key }) { return ( <div className="custom-icon"> <span className="dx-icon-box icon"></span> {key} </div> ); }
import React from 'react'; import ReactDOM from 'react-dom'; import App from './App.js'; ReactDOM.render(<App />, document.getElementById('app'));
export const ungroupedData = [ { 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', }, ]; export const pregroupedData = [ { Category: 'Video Players', Products: [ { ID: 1, Name: 'HD Video Player', }, { ID: 2, Name: 'SuperHD Player', }, ], }, { Category: 'Televisions', Products: [ { ID: 3, Name: 'SuperPlasma 50', }, { ID: 4, Name: 'SuperLED 50', }, { ID: 5, Name: 'SuperLED 42', }, { ID: 6, Name: 'SuperLCD 55', }, { ID: 7, Name: 'SuperLCD 42', }, { ID: 8, Name: 'SuperPlasma 65', }, { ID: 9, Name: 'SuperLCD 70', }, ], }, { Category: 'Monitors', Products: [ { ID: 10, Name: 'DesktopLED 21', }, { ID: 11, Name: 'DesktopLED 19', }, { ID: 12, Name: 'DesktopLCD 21', }, { ID: 13, Name: 'DesktopLCD 19', }, ], }, { Category: 'Projectors', Products: [ { ID: 14, Name: 'Projector Plus', }, { ID: 15, Name: 'Projector PlusHD', }, { ID: 16, Name: 'Projector PlusHT', }, ], }, { Category: 'Automation', Products: [ { ID: 17, Name: 'ExcelRemote IR', }, { ID: 18, Name: 'ExcelRemote BT', }, { ID: 19, Name: 'ExcelRemote IP', }, ], }, ]; export const ungroupedDataLabel = { 'aria-label': 'Ungrouped Data' }; export const pregroupedDataLabel = { 'aria-label': 'Pregrouped Data' }; export const templatedUngroupedDataLabel = { 'aria-label': 'Templated Ungrouped Data' };
<!DOCTYPE html> <html> <head> <title>DevExtreme Demo</title> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" /> <link rel="stylesheet" type="text/css" href="https://cdn3.devexpress.com/jslib/23.2.5/css/dx.light.css" /> <link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css" /> <link rel="stylesheet" type="text/css" href="styles.css" /> <script src="https://unpkg.com/core-js@2.6.12/client/shim.min.js"></script> <script src="https://unpkg.com/systemjs@0.21.3/dist/system.js"></script> <script type="text/javascript" src="config.js"></script> <script type="text/javascript"> System.import("./index.tsx"); </script> </head> <body class="dx-viewport"> <div class="demo-container"> <div id="app"></div> </div> </body> </html>
.custom-icon .icon { font-size: 17px; color: #f05b41; margin-right: 2px; }