DevExtreme v23.2 is now available.

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

Your search did not match any results.

Node Selection and Customization

Documentation

To select a node, users can click a checkbox next to it. Set the showCheckBoxesMode to "normal" or "selectAll" to display node checkboxes. The "selectAll" mode also enables a checkbox that selects all nodes simultaneously. If selectByClick is enabled, users can click nodes to select them.

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:

This demo also shows how to specify an itemTemplate for node customization. Node data is passed to the template as an argument.

Backend API
<div class="form"> <h4>Employees</h4> <dx-tree-view id="treeview" [items]="employees" [width]="340" [height]="320" [showCheckBoxesMode]="showCheckBoxesMode" [selectionMode]="selectionMode" [selectNodesRecursive]="selectNodesRecursive" [selectByClick]="selectByClick" (onSelectionChanged)="treeViewSelectionChanged($event)" (onContentReady)="treeViewContentReady($event)" > <div *dxTemplate="let item of 'item'"> {{ item.fullName + " (" + item.position + ")" }} </div> </dx-tree-view> <div class="selected-container"> Selected employees <dx-list id="selected-employees" [width]="400" [height]="200" [items]="selectedEmployees" showScrollbar="always" > <dxi-item *ngFor="let item of selectedEmployees"> {{ item.prefix + " " + item.fullName + " (" + item.position + ")" }} </dxi-item> </dx-list> </div> </div> <div class="options"> <div class="caption">Options</div> <div class="options-container"> <div class="option"> <span>Show Check Boxes Mode:</span> <div class="editor-container"> <dx-select-box [items]="showCheckBoxesModes" [inputAttr]="{ 'aria-label': 'Show Checkboxes Mode' }" [value]="showCheckBoxesMode" (onValueChanged)="showCheckBoxesModeValueChanged($event)" ></dx-select-box> </div> </div> <div class="option"> <span>Selection Mode:</span> <div class="editor-container"> <dx-select-box [items]="selectionModes" [value]="selectionMode" [inputAttr]="{ 'aria-label': 'Selection Mode' }" [disabled]="isSelectionModeDisabled" (onValueChanged)="selectionModeValueChanged($event)" ></dx-select-box> </div> </div> <div class="option"> <div class="caption-placeholder">&nbsp;</div> <div class="editor-container"> <dx-check-box text="Select Nodes Recursive" [disabled]="isRecursiveDisabled" [(value)]="selectNodesRecursive" ></dx-check-box> </div> </div> <div class="option"> <div class="caption-placeholder">&nbsp;</div> <div class="editor-container"> <dx-check-box text="Select By Click" [(value)]="selectByClick" ></dx-check-box> </div> </div> </div> </div>
import { NgModule, Component, Pipe, PipeTransform, enableProdMode, ViewChild, } from '@angular/core'; import { BrowserModule, BrowserTransferStateModule } from '@angular/platform-browser'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { DxListModule, DxTemplateModule, DxCheckBoxModule } from 'devextreme-angular'; import { DxTreeViewModule, DxTreeViewComponent, DxTreeViewTypes } from 'devextreme-angular/ui/tree-view'; import { DxSelectBoxModule, DxSelectBoxTypes } from 'devextreme-angular/ui/select-box'; import { Service, Employee } from './app.service'; @Pipe({ name: 'title' }) export class TitlePipe implements PipeTransform { transform(item: Record<string, unknown>): string { return item.text + (item.price ? ` ($${item.price})` : ''); } } if (!/localhost/.test(document.location.host)) { enableProdMode(); } @Component({ selector: 'demo-app', templateUrl: 'app/app.component.html', styleUrls: ['app/app.component.css'], providers: [Service], preserveWhitespaces: true, }) export class AppComponent { @ViewChild(DxTreeViewComponent, { static: false }) treeView: DxTreeViewComponent; employees: Employee[]; selectedEmployees: Employee[] = []; showCheckBoxesModes: DxTreeViewTypes.TreeViewCheckBoxMode[] = ['normal', 'selectAll', 'none']; showCheckBoxesMode = this.showCheckBoxesModes[0]; selectionModes: DxTreeViewTypes.SingleOrMultiple[] = ['multiple', 'single']; selectionMode = this.selectionModes[0]; selectNodesRecursive = true; selectByClick = false; isRecursiveDisabled = false; isSelectionModeDisabled = false; constructor(service: Service) { this.employees = service.getEmployees(); } treeViewSelectionChanged(e: DxTreeViewTypes.SelectionChangedEvent) { this.syncSelection(e.component); } treeViewContentReady(e: DxTreeViewTypes.ContentReadyEvent) { this.syncSelection(e.component); } syncSelection(treeView: DxTreeViewComponent['instance']) { const selectedEmployees = treeView.getSelectedNodes() .map((node) => node.itemData); this.selectedEmployees = selectedEmployees as Employee[]; } showCheckBoxesModeValueChanged(e: DxSelectBoxTypes.ValueChangedEvent) { this.showCheckBoxesMode = e.value; this.isSelectionModeDisabled = e.value === 'selectAll'; if (e.value === 'selectAll') { this.selectionMode = 'multiple'; this.isRecursiveDisabled = false; } } selectionModeValueChanged(e: DxSelectBoxTypes.ValueChangedEvent) { this.selectionMode = e.value; this.isRecursiveDisabled = e.value === 'single'; if (e.value === 'single') { this.selectNodesRecursive = false; this.treeView.instance.unselectAll(); } } } @NgModule({ imports: [ BrowserModule, BrowserTransferStateModule, DxTreeViewModule, DxListModule, DxTemplateModule, DxCheckBoxModule, DxSelectBoxModule, ], declarations: [AppComponent, TitlePipe], bootstrap: [AppComponent], }) export class AppModule { } platformBrowserDynamic().bootstrapModule(AppModule);
::ng-deep .form > h4 { margin-bottom: 20px; } ::ng-deep .form > div, ::ng-deep #treeview { display: inline-block; vertical-align: top; } ::ng-deep .selected-container { padding: 20px; margin-left: 20px; background-color: rgba(191, 191, 191, 0.15); font-size: 115%; font-weight: bold; } ::ng-deep #selected-employees { margin-top: 20px; } ::ng-deep .selected-container .dx-list-item-content { padding-left: 0; } ::ng-deep .options { padding: 20px; background-color: rgba(191, 191, 191, 0.15); margin-top: 20px; } ::ng-deep .caption { font-size: 18px; font-weight: 500; } ::ng-deep .option { width: 24%; margin-top: 10px; margin-right: 9px; box-sizing: border-box; display: flex; flex-direction: column; justify-content: center; } ::ng-deep .options-container { display: flex; justify-content: space-between; align-items: stretch; } ::ng-deep .editor-container { height: 100%; display: flex; align-items: center; } ::ng-deep .editor-container > * { width: 100%; } ::ng-deep .option:last-of-type { margin-right: 0; }
import { Injectable } from '@angular/core'; export class Employee { id: number; fullName: string; prefix: string; position: string; expanded?: boolean; selected?: boolean; items?: Employee[]; } const employees: Employee[] = [{ 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', }], }], }]; @Injectable() export class Service { getEmployees(): Employee[] { return employees; } }
// In real applications, you should not transpile code in the browser. // You can see how to create your own application with Angular and DevExtreme here: // https://js.devexpress.com/Documentation/Guide/Angular_Components/Getting_Started/Create_a_DevExtreme_Application/ window.exports = window.exports || {}; window.config = { transpiler: 'ts', typescriptOptions: { module: 'system', emitDecoratorMetadata: true, experimentalDecorators: true, }, meta: { '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/', }, map: { 'ts': 'npm:plugin-typescript@4.2.4/lib/plugin.js', 'typescript': 'npm:typescript@4.2.4/lib/typescript.js', '@angular/core': 'npm:@angular/core@12.2.17', '@angular/platform-browser': 'npm:@angular/platform-browser@12.2.17', '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic@12.2.17', '@angular/forms': 'npm:@angular/forms@12.2.17', '@angular/common': 'npm:@angular/common@12.2.17', '@angular/compiler': 'npm:@angular/compiler@12.2.17', 'tslib': 'npm:tslib@2.6.2/tslib.js', 'rxjs': 'npm:rxjs@7.5.3/dist/bundles/rxjs.umd.js', 'rxjs/operators': 'npm:rxjs@7.5.3/dist/cjs/operators/index.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/bundles/dx.all': 'npm:devextreme@23.2.5/bundles/dx.all.js', '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', 'devexpress-gantt': 'npm:devexpress-gantt@4.1.51', 'devextreme-angular': 'npm:devextreme-angular@23.2.5', '@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', // Prettier 'prettier/standalone': 'npm:prettier@2.8.4/standalone.js', 'prettier/parser-html': 'npm:prettier@2.8.4/parser-html.js', }, packages: { 'app': { main: './app.component.ts', defaultExtension: 'ts', }, 'devextreme': { defaultExtension: 'js', }, 'devextreme/events/utils': { main: 'index', }, 'devextreme/events': { main: 'index', }, 'es6-object-assign': { main: './index.js', defaultExtension: 'js', }, 'rxjs': { defaultExtension: 'js', }, 'rxjs/operators': { defaultExtension: 'js', }, }, packageConfigPaths: [ 'npm:@devextreme/*/package.json', 'npm:@devextreme/runtime@3.0.12/inferno/package.json', 'npm:@angular/*/package.json', 'npm:@angular/common@12.2.17/*/package.json', 'npm:rxjs@7.5.3/package.json', 'npm:rxjs@7.5.3/operators/package.json', 'npm:devextreme-angular@23.2.5/*/package.json', 'npm:devextreme-angular@23.2.5/ui/*/package.json', 'npm:devextreme-angular@23.2.5/package.json', 'npm:devexpress-diagram@2.2.5/package.json', 'npm:devexpress-gantt@4.1.51/package.json', ], }; System.config(window.config);
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <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" /> <script src="https://unpkg.com/core-js@2.6.12/client/shim.min.js"></script> <script src="https://unpkg.com/zone.js@0.12.0/dist/zone.js"></script> <script src="https://unpkg.com/reflect-metadata@0.1.13/Reflect.js"></script> <script src="https://unpkg.com/systemjs@0.21.3/dist/system.js"></script> <script src="config.js"></script> <script> System.import("app").catch(console.error.bind(console)); </script> </head> <body class="dx-viewport"> <div class="demo-container"> <demo-app>Loading...</demo-app> </div> </body> </html>