DevExtreme v25.2 is now available.

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

Your search did not match any results.

Angular Tree List - AI Columns

DevExtreme TreeList allows you to add multiple AI columns to the TreeList. These columns auto generate meaningful cell values based on component data and a custom prompt, transforming DevExtreme TreeList into an AI-powered data analysis tool. In this demo, an AI column is fixed to the right side of the component.

Backend API
<dx-tree-list id="employees" keyExpr="ID" parentIdExpr="Head_ID" [dataSource]="employees" [autoExpandAll]="true" [showBorders]="true" [aiIntegration]="aiIntegration" (onAIColumnRequestCreating)="onAIColumnRequestCreating($event)" > <dxo-tree-list-scrolling mode="standard" /> <dxo-tree-list-paging [enabled]="true" [pageSize]="10" /> <dxi-tree-list-column caption="Employee" cssClass="employee__cell" [width]="260" cellTemplate="employeeTemplate" /> <div *dxTemplate="let model of 'employeeTemplate'"> <employee [firstName]="model.data.First_Name" [lastName]="model.data.Last_Name" /> </div> <dxi-tree-list-column dataField="Title" caption="Position" [width]="140" /> <dxi-tree-list-column dataField="Status" [minWidth]="180" cellTemplate="statusTemplate" /> <div *dxTemplate="let model of 'statusTemplate'"> <status [status]="model.data.Status" /> </div> <dxi-tree-list-column dataField="City" [width]="180" /> <dxi-tree-list-column dataField="State" [width]="140" /> <dxi-tree-list-column dataField="Email" [minWidth]="200" /> <dxi-tree-list-column name="AI Column" caption="AI Column" type="ai" cssClass="ai__cell" [fixed]="true" fixedPosition="right" [width]="180" > <dxo-tree-list-ai prompt='Identify the department where the employee works. Select from the following department list: "Management", "Human Resources", "IT", "Shipping", "Support", "Sales", "Engineering". Use "Engineering" if you cannot find a better match.' mode="auto" noDataText="No data" /> </dxi-tree-list-column> </dx-tree-list>
import { bootstrapApplication } from '@angular/platform-browser'; import { Component, enableProdMode, provideZoneChangeDetection } from '@angular/core'; import { DxTreeListModule } from 'devextreme-angular'; import type { AIIntegration } from 'devextreme-angular/common/ai-integration'; import { Service, type IEmployee } from './app.service'; import { AiService } from './ai/ai.service'; import { Employee } from './employee/employee.component'; import { Status } from './status/status.component'; if (!/localhost/.test(document.location.host)) { enableProdMode(); } let modulePrefix = ''; // @ts-ignore if (window && window.config?.packageConfigPaths) { modulePrefix = '/app'; } @Component({ selector: 'demo-app', templateUrl: `app/app.component.html`, styleUrls: [`app/app.component.css`], providers: [Service, AiService], imports: [ DxTreeListModule, Employee, Status, ], }) export class AppComponent { employees: IEmployee[]; aiIntegration: AIIntegration; constructor(service: Service, aiService: AiService) { this.employees = service.getEmployees(); this.aiIntegration = aiService.getAiIntegration(); } onAIColumnRequestCreating(e: { data: Partial<IEmployee>[] }) { e.data = e.data.map((item) => ({ ID: item.ID, First_Name: item.First_Name, Last_Name: item.Last_Name, Title: item.Title, })); } } bootstrapApplication(AppComponent, { providers: [ provideZoneChangeDetection({ eventCoalescing: true, runCoalescing: true }), ], });
::ng-deep #employees { min-height: 560px; } ::ng-deep #employees .ai__cell { background-color: var(--dx-datagrid-row-alternation-bg); } ::ng-deep #employees .employee__cell > div { align-items: flex-end; }
import { Injectable } from '@angular/core'; export interface IEmployee { ID: number; Head_ID: number; First_Name: string; Last_Name: string; Prefix: string; Title: string; City: string; State: string; Email: string; Skype?: string; Mobile_Phone: string; Birth_Date: string; Hire_Date: string; Status: string; } export const employees: IEmployee[] = [{ ID: 1, Head_ID: 0, First_Name: 'John', Last_Name: 'Heart', Prefix: 'Mr.', Title: 'CEO', City: 'Los Angeles', State: 'California', Email: 'jheart@dx-email.com', Skype: 'jheart_DX_skype', Mobile_Phone: '(213) 555-9392', Birth_Date: '1964-03-16', Hire_Date: '1995-01-15', Status: 'Salaried', }, { ID: 3, Head_ID: 1, First_Name: 'Arthur', Last_Name: 'Miller', Prefix: 'Mr.', Title: 'CTO', City: 'Los Angeles', State: 'California', Email: 'arthurm@dx-email.com', Mobile_Phone: '+1 (310) 555-8583', Birth_Date: '1972-07-11', Hire_Date: '2007-12-18', Status: 'Salaried', }, { ID: 4, Head_ID: 1, First_Name: 'Robert', Last_Name: 'Reagan', Prefix: 'Mr.', Title: 'CMO', City: 'Pasadena', State: 'California', Email: 'robertr@dx-email.com', Mobile_Phone: '+1 (818) 555-2387', Birth_Date: '1974-09-07', Hire_Date: '2002-11-08', Status: 'Salaried', }, { ID: 5, Head_ID: 1, First_Name: 'Greta', Last_Name: 'Sims', Prefix: 'Ms.', Title: 'HR Manager', City: 'Alhambra', State: 'California', Email: 'gretas@dx-email.com', Mobile_Phone: '+1 (818) 555-6546', Birth_Date: '1977-11-22', Hire_Date: '1998-04-23', Status: 'Salaried', }, { ID: 6, Head_ID: 3, First_Name: 'Brett', Last_Name: 'Wade', Prefix: 'Mr.', Title: 'IT Manager', City: 'San Marino', State: 'California', Email: 'brettw@dx-email.com', Mobile_Phone: '+1 (626) 555-0358', Birth_Date: '1968-12-01', Hire_Date: '2009-03-06', Status: 'Salaried', }, { ID: 7, Head_ID: 5, First_Name: 'Sandra', Last_Name: 'Johnson', Prefix: 'Mrs.', Title: 'Controller', City: 'Long Beach', State: 'California', Email: 'sandraj@dx-email.com', Mobile_Phone: '+1 (562) 555-2082', Birth_Date: '1974-11-15', Hire_Date: '2005-05-11', Status: 'Salaried', }, { ID: 21, Head_ID: 6, First_Name: 'Taylor', Last_Name: 'Riley', Prefix: 'Mr.', Title: 'Network Admin', City: 'West Hollywood', State: 'California', Email: 'taylorr@dx-email.com', Mobile_Phone: '+1 (310) 555-7276', Birth_Date: '1982-08-14', Hire_Date: '2012-04-14', Status: 'Salaried', }, { ID: 22, Head_ID: 6, First_Name: 'Amelia', Last_Name: 'Harper', Prefix: 'Mrs.', Title: 'Network Admin', City: 'Los Angeles', State: 'California', Email: 'ameliah@dx-email.com', Mobile_Phone: '+1 (213) 555-4276', Birth_Date: '1983-11-19', Hire_Date: '2011-02-10', Status: 'Salaried', }, { ID: 25, Head_ID: 6, First_Name: 'Karen', Last_Name: 'Goodson', Prefix: 'Miss', Title: 'Programmer', City: 'South Pasadena', State: 'California', Email: 'kareng@dx-email.com', Mobile_Phone: '+1 (626) 555-0908', Birth_Date: '1987-04-26', Hire_Date: '2011-03-14', Status: 'Salaried', }, { ID: 28, Head_ID: 6, First_Name: 'Morgan', Last_Name: 'Kennedy', Prefix: 'Mrs.', Title: 'Graphic Designer', City: 'San Fernando Valley', State: 'California', Email: 'morgank@dx-email.com', Mobile_Phone: '+1 (818) 555-8238', Birth_Date: '1984-07-17', Hire_Date: '2012-01-11', Status: 'Salaried', }, { ID: 29, Head_ID: 28, First_Name: 'Violet', Last_Name: 'Bailey', Prefix: 'Ms.', Title: 'Jr Graphic Designer', City: 'La Canada', State: 'California', Email: 'violetb@dx-email.com', Mobile_Phone: '+1 (818) 555-2478', Birth_Date: '1985-06-10', Hire_Date: '2012-01-19', Status: 'Salaried', }, { ID: 32, Head_ID: 3, First_Name: 'Bart', Last_Name: 'Arnaz', Prefix: 'Mr.', Title: 'Director of Engineering', City: 'Irvine', State: 'California', Email: 'barta@dx-email.com', Mobile_Phone: '+1 (714) 555-2000', Birth_Date: '1979-11-01', Hire_Date: '2008-06-29', Status: 'Salaried', }, { ID: 36, Head_ID: 32, First_Name: 'Samantha', Last_Name: 'Piper', Prefix: 'Ms.', Title: 'Engineer', City: 'Los Angeles', State: 'California', Email: 'samanthap@dx-email.com', Mobile_Phone: '+1 (323) 555-4512', Birth_Date: '1984-12-01', Hire_Date: '2008-01-22', Status: 'Salaried', }, { ID: 38, Head_ID: 32, First_Name: 'Terry', Last_Name: 'Bradley', Prefix: 'Mr.', Title: 'QA Engineer', City: 'Simi Valley', State: 'California', Email: 'terryb@dx-email.com', Mobile_Phone: '+1 (805) 555-2788', Birth_Date: '1984-01-09', Hire_Date: '2007-10-18', Status: 'Salaried', }, { ID: 44, Head_ID: 4, First_Name: 'Clark', Last_Name: 'Morgan', Prefix: 'Mr.', Title: 'Retail Sales Manager', City: 'Martinez', State: 'California', Email: 'clarkm@dx-email.com', Mobile_Phone: '+1 (925) 555-2525', Birth_Date: '1988-04-07', Hire_Date: '2012-04-11', Status: 'Commission', }, { ID: 45, Head_ID: 4, First_Name: 'Todd', Last_Name: 'Hoffman', Prefix: 'Mr.', Title: 'Retail Sales Manager', City: 'Livermore', State: 'California', Email: 'toddh@dx-email.com', Mobile_Phone: '+1 (925) 555-3579', Birth_Date: '1987-03-25', Hire_Date: '2012-04-19', Status: 'Commission', }, { ID: 48, Head_ID: 32, First_Name: 'Brad', Last_Name: 'Farkus', Prefix: 'Mr.', Title: 'Engineer', City: 'Los Angeles', State: 'California', Email: 'bradf@dx-email.com', Mobile_Phone: '+1 (213) 555-3626', Birth_Date: '1991-03-17', Hire_Date: '2010-04-15', Status: 'Terminated', }, { ID: 51, Head_ID: 32, First_Name: 'Stu', Last_Name: 'Pizaro', Prefix: 'Mr.', Title: 'Engineer', City: 'Los Angeles', State: 'California', Email: 'stu@dx-email.com', Mobile_Phone: '+1 (213) 555-2552', Birth_Date: '1985-09-11', Hire_Date: '2011-07-19', Status: 'Terminated', }]; @Injectable() export class Service { getEmployees() { 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/ const componentNames = [ 'accordion', 'action-sheet', 'autocomplete', 'bar-gauge', 'box', 'bullet', 'button-group', 'button', 'calendar', 'card-view', 'chart', 'chat', 'check-box', 'circular-gauge', 'color-box', 'context-menu', 'data-grid', 'date-box', 'date-range-box', 'defer-rendering', 'diagram', 'draggable', 'drawer', 'drop-down-box', 'drop-down-button', 'file-manager', 'file-uploader', 'filter-builder', 'form', 'funnel', 'gallery', 'gantt', 'html-editor', 'linear-gauge', 'list', 'load-indicator', 'load-panel', 'lookup', 'map', 'menu', 'multi-view', 'nested', 'number-box', 'pagination', 'pie-chart', 'pivot-grid-field-chooser', 'pivot-grid', 'polar-chart', 'popover', 'popup', 'progress-bar', 'radio-group', 'range-selector', 'range-slider', 'recurrence-editor', 'resizable', 'responsive-box', 'sankey', 'scheduler', 'scroll-view', 'select-box', 'slider', 'sortable', 'sparkline', 'speech-to-text', 'speed-dial-action', 'splitter', 'stepper', 'switch', 'tab-panel', 'tabs', 'tag-box', 'text-area', 'text-box', 'tile-view', 'toast', 'toolbar', 'tooltip', 'tree-list', 'tree-map', 'tree-view', 'validation-group', 'validation-summary', 'validator', 'vector-map', ]; 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, }, '@angular/platform-browser-dynamic': { 'esModule': true, }, '@angular/platform-browser': { 'esModule': true, }, '@angular/core': { 'esModule': true, }, '@angular/common': { 'esModule': true, }, '@angular/common/http': { 'esModule': true, }, '@angular/animations': { 'esModule': true, }, '@angular/forms': { 'esModule': true, }, 'openai': { 'esModule': true, }, }, paths: { 'npm:': 'https://cdn.jsdelivr.net/npm/', 'bundles:': '../../../../bundles/', 'externals:': '../../../../bundles/externals/', }, 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', /* @angular */ '@angular/compiler': 'bundles:@angular/compiler.umd.js', '@angular/platform-browser-dynamic': 'bundles:@angular/platform-browser-dynamic.umd.js', '@angular/core': 'bundles:@angular/core.umd.js', '@angular/core/primitives/signals': 'bundles:@angular/core.primitives.signals.umd.js', '@angular/core/primitives/di': 'bundles:@angular/core.primitives.di.umd.js', '@angular/common': 'bundles:@angular/common.umd.js', '@angular/common/http': 'bundles:@angular/common-http.umd.js', '@angular/platform-browser': 'bundles:@angular/platform-browser.umd.js', '@angular/platform-browser/animations': 'bundles:@angular/platform-browser.umd.js', '@angular/forms': 'bundles:@angular/forms.umd.js', /* devextreme */ 'devextreme': 'npm:devextreme@link:../../packages/devextreme/artifacts/npm/devextreme/cjs', 'devextreme-quill': 'npm:devextreme-quill@1.7.8/dist/dx-quill.min.js', 'devexpress-diagram': 'npm:devexpress-diagram@2.2.24', 'devexpress-gantt': 'npm:devexpress-gantt@4.1.64', /* devextreme-angular umd maps */ 'devextreme-angular': 'bundles:devextreme-angular/devextreme-angular.umd.js', 'devextreme-angular/common/ai-integration': 'bundles:devextreme-angular/devextreme-angular-common-ai-integration.umd.js', 'devextreme-angular/core': 'bundles:devextreme-angular/devextreme-angular-core.umd.js', 'devextreme-angular/common/charts': 'bundles:devextreme-angular/devextreme-angular-common-charts.umd.js', 'devextreme-angular/common/core/animation': 'bundles:devextreme-angular/devextreme-angular-common-core-animation.umd.js', 'devextreme-angular/common/core/environment': 'bundles:devextreme-angular/devextreme-angular-common-core-environment.umd.js', 'devextreme-angular/common/core/events': 'bundles:devextreme-angular/devextreme-angular-common-core-events.umd.js', 'devextreme-angular/common/core/localization': 'bundles:devextreme-angular/devextreme-angular-common-core-localization.umd.js', 'devextreme-angular/common/core': 'bundles:devextreme-angular/devextreme-angular-common-core.umd.js', 'devextreme-angular/common/data/custom-store': 'bundles:devextreme-angular/devextreme-angular-common-data-custom-store.umd.js', 'devextreme-angular/common/data': 'bundles:devextreme-angular/devextreme-angular-common-data.umd.js', 'devextreme-angular/common/export/excel': 'bundles:devextreme-angular/devextreme-angular-common-export-excel.umd.js', 'devextreme-angular/common/export/pdf': 'bundles:devextreme-angular/devextreme-angular-common-export-pdf.umd.js', 'devextreme-angular/common/export': 'bundles:devextreme-angular/devextreme-angular-common-export.umd.js', 'devextreme-angular/common/grids': 'bundles:devextreme-angular/devextreme-angular-common-grids.umd.js', 'devextreme-angular/common': 'bundles:devextreme-angular/devextreme-angular-common.umd.js', 'devextreme-angular/http': 'bundles:devextreme-angular/devextreme-angular-http.umd.js', 'devextreme-angular/core/tokens': 'bundles:devextreme-angular/devextreme-angular-core-tokens.umd.js', ...componentNames.reduce((acc, name) => { acc[`devextreme-angular/ui/${name}`] = `bundles:devextreme-angular/devextreme-angular-ui-${name}.umd.js`; acc[`devextreme-angular/ui/${name}/nested`] = `bundles:devextreme-angular/devextreme-angular-ui-${name}-nested.umd.js`; return acc; }, {}), 'openai': 'externals:openai.bundle.js', 'tslib': 'npm:tslib/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@3.4.4/build/global/luxon.min.js', 'es6-object-assign': 'npm:es6-object-assign', 'inferno': 'npm:inferno@8.2.3/dist/inferno.min.js', 'inferno-compat': 'npm:inferno-compat/dist/inferno-compat.min.js', 'inferno-create-element': 'npm:inferno-create-element@8.2.3/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', '@preact/signals-core': 'npm:@preact/signals-core@1.8.0/dist/signals-core.min.js', // Prettier 'prettier/standalone': 'npm:prettier@2.8.8/standalone.js', 'prettier/parser-html': 'npm:prettier@2.8.8/parser-html.js', 'zone.js': 'npm:zone.js@0.15.1/bundles/zone.umd.js', }, packages: { 'app': { main: './app.component.ts', defaultExtension: 'ts', }, 'devextreme': { defaultExtension: 'js', }, 'devextreme/events/utils': { main: 'index', }, 'devextreme/common/core/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:rxjs@7.5.3/package.json', 'npm:rxjs@7.5.3/operators/package.json', 'npm:devexpress-diagram@2.2.24/package.json', 'npm:devexpress-gantt@4.1.64/package.json', ], }; window.process = { env: { NODE_ENV: 'production', }, }; System.config(window.config); // eslint-disable-next-line no-console // System.import('@angular/compiler').catch(console.error.bind(console)); // eslint-disable-next-line const useTgzInCSB = ['openai']; let packagesInfo = { "openai": { "version": "4.73.1" }, "@angular/core": { "version": "21.0.7" }, "core-js": { "version": "2.6.12" }, "typescript": { "version": "5.9.3" }, "zone.js": { "version": "0.15.1" } };
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" lang="en"> <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=5.0" /> <link rel="stylesheet" type="text/css" href="https://cdn3.devexpress.com/jslib/25.2.4/css/dx.light.css" /> <script src="https://cdn.jsdelivr.net/npm/core-js@2.6.12/client/shim.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/zone.js@0.15.1/bundles/zone.umd.js"></script> <script src="https://cdn.jsdelivr.net/npm/reflect-metadata@0.1.13/Reflect.js"></script> <script src="https://cdn.jsdelivr.net/npm/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>
import { AzureOpenAI, OpenAI } from 'openai'; import { Injectable } from '@angular/core'; import notify from 'devextreme/ui/notify'; import { AIIntegration, type RequestParams, type Response, } from 'devextreme-angular/common/ai-integration'; type AIMessage = (OpenAI.ChatCompletionUserMessageParam | OpenAI.ChatCompletionSystemMessageParam) & { content: string; }; const AzureOpenAIConfig = { dangerouslyAllowBrowser: true, deployment: 'gpt-4o-mini', apiVersion: '2024-02-01', endpoint: 'https://public-api.devexpress.com/demo-openai', apiKey: 'DEMO', }; const RATE_LIMIT_RETRY_DELAY_MS = 30000; const MAX_PROMPT_SIZE = 5000; const service = new AzureOpenAI(AzureOpenAIConfig); async function getAIResponse(messages: AIMessage[], signal: AbortSignal) { const params = { messages, model: AzureOpenAIConfig.deployment, max_tokens: 1000, temperature: 0.7, }; const response = await service.chat.completions.create(params, { signal }); const result = response.choices[0].message?.content; if (!result) { throw new Error('AI response returned empty content'); } return result; } async function getAIResponseRecursive(messages: AIMessage[], signal: AbortSignal): Promise<string> { return getAIResponse(messages, signal) .catch(async (error) => { if (!error.message.includes('Connection error')) { return Promise.reject(error); } notify({ message: 'Our demo AI service reached a temporary request limit. Retrying in 30 seconds.', width: 'auto', type: 'error', displayTime: 5000, }); await new Promise((resolve) => setTimeout(resolve, RATE_LIMIT_RETRY_DELAY_MS)); return getAIResponseRecursive(messages, signal); }); } const aiIntegration = new AIIntegration({ sendRequest({ prompt }: RequestParams): Response { const isValidRequest = JSON.stringify(prompt.user).length < MAX_PROMPT_SIZE; if (!isValidRequest) { return { promise: Promise.reject(new Error('Request is too long. Specify a shorter prompt.')), abort: () => {}, }; } const controller = new AbortController(); const signal = controller.signal; if (!prompt.user || !prompt.system) { throw new Error('Invalid prompt data'); } const aiPrompt: AIMessage[] = [ { role: 'system', content: prompt.system }, { role: 'user', content: prompt.user }, ]; const promise = getAIResponseRecursive(aiPrompt, signal); const result: Response = { promise, abort: () => { controller.abort(); }, }; return result; }, }); @Injectable() export class AiService { getAiIntegration() { return aiIntegration; } }
.employee__wrapper { display: flex; align-items: center; gap: 8px; } .employee__img-wrapper { width: 40px; height: 40px; border: var(--dx-border-width) solid var(--dx-color-border); border-radius: 50%; cursor: pointer; } .employee__img-wrapper img { width: 100%; height: 100%; object-fit: cover; object-position: center; border-radius: 50%; } .employee__text-wrapper { width: calc(100% - 48px); } .employee__text { margin: 0; padding: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
<div class="employee__wrapper"> <div class="employee__img-wrapper"> <img src="../../../../images/employees/new/{{ firstName }} {{ lastName }}.jpg" alt="{{ firstName }} {{ lastName }}" /> </div> <div class="employee__text-wrapper"> <div class="employee__text">{{ firstName }}</div> <div class="employee__text">{{ lastName }}</div> </div> </div>
import { Component, Input } from '@angular/core'; let modulePrefix = ''; // @ts-ignore if (window && window.config?.packageConfigPaths) { modulePrefix = '/app'; } @Component({ selector: 'employee', templateUrl: `app/employee/employee.component.html`, styleUrls: [`app/employee/employee.component.css`], }) export class Employee { @Input() firstName!: string; @Input() lastName!: string; }
.status { display: flex; align-items: center; } .status--salaried { color: var(--dx-color-success); } .status--commission { color: #f7630c; } .status--terminated { color: var(--dx-color-danger); } .status__indicator { background-color: currentcolor; margin-right: 8px; border-radius: 50%; height: 12px; width: 12px; }
<div class="status status--{{ status.toLowerCase() }}"> <div class="status__indicator"></div> <div>{{ status }}</div> </div>
import { Component, Input } from '@angular/core'; let modulePrefix = ''; // @ts-ignore if (window && window.config?.packageConfigPaths) { modulePrefix = '/app'; } @Component({ selector: 'status', templateUrl: `app/status/status.component.html`, styleUrls: [`app/status/status.component.css`], }) export class Status { @Input() status!: string; }

AI services used for this demo have been rate and data limited. As such, you may experience performance-related delays when exploring the capabilities of TreeList AI Columns.

When connected to your own AI model/service without rate and data limits, TreeList AI Columns will perform seamlessly, without artificial delays. Note that DevExtreme does not offer an AI REST API and does not ship any built-in LLMs/SLMs.

This demo instructs the AI service to identify the department name associated with each employee. You can modify the default prompt or enter a custom prompt in the AI column header menu.

To integrate an AI column into the DevExtreme TreeList, you must:

  • Configure the aiIntegration property at the component or column level (aiIntegration or columns[].ai.aiIntegration).
  • Set the column type to "ai".
  • Specify the column name.
  • Configure columns[].ai options, such as generation mode, predefined prompt, and no data text (displayed when the AI service returns no data for a row).

Our DevExtreme TreeList component uses all visible row data in AI requests, including fields not bound to a column and hidden column fields. This data gives LLMs broader context, but increases the use of AI resources. To limit data included in AI requests, modify the AIColumnRequestCreatingEvent.data parameter in the onAIColumnRequestCreating event handler.