DevExtreme v24.1 is now available.

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

Your search did not match any results.

Angular Gantt - Task Template

The DevExtreme Gantt templates help customize the layout and appearance of individual task elements. Templates allow you to mix HTML code with any DevExtreme JavaScript component - and position desired elements within the container. In this demo, a template is used to display images within tasks and change their background color.

Backend API
<div id="demo-container"> <div class="widget-container"> <dx-gantt taskListWidth="500" height="700" scaleType="days" taskContentTemplate="taskContentTemplate" > <dxo-tasks [dataSource]="tasks"></dxo-tasks> <dxo-dependencies [dataSource]="dependencies"></dxo-dependencies> <dxo-resources [dataSource]="resources"></dxo-resources> <dxo-resource-assignments [dataSource]="resourceAssignments" ></dxo-resource-assignments> <dxi-column dataField="title" caption="Subject" [width]="300" ></dxi-column> <dxi-column dataField="start" caption="Start Date" dataType="date" ></dxi-column> <dxi-column dataField="end" caption="End Date" dataType="date" ></dxi-column> <dxo-editing [enabled]="true"></dxo-editing> <div *dxTemplate="let item of 'taskContentTemplate'" class="custom-task {{ getTaskColor(item.taskData.id) }}" style.width="{{ item.taskSize.width }}px" > <div class="custom-task-img-wrapper"> <img class="custom-task-img" attr.src="{{ getImagePath(item.taskData.id) }}" /> </div> <div class="custom-task-wrapper"> <div class="custom-task-title">{{ item.taskData.title }}</div> <div class="custom-task-row">{{ item.taskResources[0].text }}</div> </div> <div class="custom-task-progress" style.width="{{ item.taskData.progress }}%" ></div> </div> </dx-gantt> </div> </div>
import { NgModule, Component, enableProdMode, ViewEncapsulation } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { DxGanttModule, DxCheckBoxModule, DxSelectBoxModule } from 'devextreme-angular'; import { Service, Task, Dependency, Resource, ResourceAssignment, } from './app.service'; 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, encapsulation: ViewEncapsulation.None, }) export class AppComponent { tasks: Task[]; dependencies: Dependency[]; resources: Resource[]; resourceAssignments: ResourceAssignment[]; constructor(service: Service) { this.tasks = service.getTasks(); this.dependencies = service.getDependencies(); this.resources = service.getResources(); this.resourceAssignments = service.getResourceAssignments(); } getImagePath(taskId) { const imgPath = '../../../../images/employees'; let img = taskId < 10 ? `0${taskId}` : taskId; img = `${imgPath}/${img}.png`; return img; } getTaskColor(taskId) { const color = taskId % 6; return `custom-task-color-${color}`; } } @NgModule({ imports: [ BrowserModule, DxGanttModule, DxCheckBoxModule, DxSelectBoxModule, ], declarations: [AppComponent], bootstrap: [AppComponent], }) export class AppModule { } platformBrowserDynamic().bootstrapModule(AppModule);
#gantt { height: 700px; } .custom-task-color-0 { background-color: #5c57c9; } .custom-task-color-1 { background-color: #35b86b; } .custom-task-color-2 { background-color: #4796ce; } .custom-task-color-3 { background-color: #ce4776; } .custom-task-color-4 { background-color: #ce5b47; } .custom-task-color-5 { background-color: #f78119; } .custom-task-color-6 { background-color: #9f47ce; } .custom-task { max-height: 48px; height: 100%; display: block; overflow: hidden; } .custom-task-wrapper { padding: 8px; color: #fff; } .custom-task-wrapper > * { display: block; overflow: hidden; text-overflow: ellipsis; } .custom-task-img-wrapper { float: left; width: 32px; height: 32px; border-radius: 50%; margin: 8px; background-color: #fff; overflow: hidden; } .custom-task-img { width: 32px; } .custom-task-title { font-weight: 600; font-size: 13px; } .custom-task-row { font-size: 11px; } .custom-task-progress { position: absolute; left: 0; bottom: 0; width: 0%; height: 4px; background: rgba(0, 0, 0, 0.3); } .dx-gantt .dx-row { height: 63px; }
import { Injectable } from '@angular/core'; export class Task { id: number; parentId: number; title: string; start: Date; end: Date; progress: number; } export class Dependency { id: number; predecessorId: number; successorId: number; type: number; } export class Resource { id: number; text: string; } export class ResourceAssignment { id: number; taskId: number; resourceId: number; } const currentDate: Date = new Date(Date.now()); const month: number = currentDate.getMonth(); const year: number = currentDate.getFullYear(); const tasks: Task[] = [{ id: 1, parentId: 0, title: 'Analysis/Software Requirements', start: new Date(year, month, 1), end: new Date(year, month, 28), progress: 31, }, { id: 2, parentId: 1, title: 'Conduct needs analysis', start: new Date(year, month, 1), end: new Date(year, month, 3), progress: 15, }, { id: 3, parentId: 1, title: 'Draft preliminary software specifications', start: new Date(year, month, 3), end: new Date(year, month, 5), progress: 30, }, { id: 4, parentId: 1, title: 'Review software specifications/budget with team', start: new Date(year, month, 4), end: new Date(year, month, 6), progress: 60, }, { id: 5, parentId: 1, title: 'Incorporate feedback on software specifications', start: new Date(year, month, 6), end: new Date(year, month, 8), progress: 45, }, { id: 6, parentId: 1, title: 'Develop delivery timeline', start: new Date(year, month, 8), end: new Date(year, month, 14), progress: 15, }, { id: 7, parentId: 1, title: 'Obtain approvals to proceed (concept, timeline, budget)', start: new Date(year, month, 14), end: new Date(year, month, 20), progress: 15, }, { id: 8, parentId: 1, title: 'Draft preliminary software specifications', start: new Date(year, month, 20), end: new Date(year, month, 25), progress: 0, }, { id: 9, parentId: 1, title: 'Secure required resources', start: new Date(year, month, 25), end: new Date(year, month, 28), progress: 0, }]; const dependencies: Dependency[] = [{ id: 1, predecessorId: 2, successorId: 3, type: 0, }, { id: 2, predecessorId: 3, successorId: 4, type: 0, }, { id: 3, predecessorId: 4, successorId: 5, type: 0, }, { id: 4, predecessorId: 5, successorId: 6, type: 0, }, { id: 5, predecessorId: 6, successorId: 7, type: 0, }, { id: 6, predecessorId: 7, successorId: 8, type: 0, }, { id: 7, predecessorId: 8, successorId: 9, type: 0, }]; const resources: Resource[] = [{ id: 1, text: 'John Heart', }, { id: 2, text: 'Paul Peyton', }, { id: 3, text: 'Robert Reagan', }, { id: 4, text: 'Greta Sims', }, { id: 5, text: 'Brett Wade', }, { id: 6, text: 'Sandra Johnson', }, { id: 7, text: 'Kevin Carter', }, { id: 8, text: 'Cynthia Stanwick', }, { id: 9, text: 'Olivia Samuelson', }]; const resourceAssignments: ResourceAssignment[] = [{ id: 0, taskId: 1, resourceId: 1, }, { id: 1, taskId: 2, resourceId: 2, }, { id: 2, taskId: 3, resourceId: 3, }, { id: 3, taskId: 4, resourceId: 4, }, { id: 4, taskId: 5, resourceId: 5, }, { id: 5, taskId: 6, resourceId: 6, }, { id: 6, taskId: 7, resourceId: 7, }, { id: 7, taskId: 8, resourceId: 8, }, { id: 8, taskId: 9, resourceId: 9, }]; @Injectable() export class Service { getTasks(): Task[] { return tasks; } getDependencies(): Dependency[] { return dependencies; } getResources(): Resource[] { return resources; } getResourceAssignments(): ResourceAssignment[] { return resourceAssignments; } }
// 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', 'chart', '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', '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', 'speed-dial-action', 'splitter', '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/compiler': { 'esModule': true, }, '@angular/animations': { 'esModule': true, }, '@angular/forms': { 'esModule': true, }, }, paths: { 'npm:': 'https://unpkg.com/', 'bundles:': '../../../../bundles/', }, map: { 'ts': 'npm:plugin-typescript@4.2.4/lib/plugin.js', 'typescript': 'npm:typescript@4.2.4/lib/typescript.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/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@24.1.7/cjs', '@devextreme/runtime': 'npm:@devextreme/runtime@3.0.13', 'devextreme/bundles/dx.all': 'npm:devextreme@24.1.7/bundles/dx.all.js', 'devextreme-quill': 'npm:devextreme-quill@1.7.1/dist/dx-quill.min.js', 'devexpress-diagram': 'npm:devexpress-diagram@2.2.13', 'devexpress-gantt': 'npm:devexpress-gantt@4.1.56', /* devextreme-angular umd maps */ 'devextreme-angular': 'bundles:devextreme-angular/devextreme-angular.umd.js', 'devextreme-angular/core': 'bundles:devextreme-angular/devextreme-angular-core.umd.js', 'devextreme-angular/http': 'bundles:devextreme-angular/devextreme-angular-http.umd.js', ...componentNames.reduce((acc, name) => { acc[`devextreme-angular/ui/${name}`] = `bundles:devextreme-angular/devextreme-angular-ui-${name}.umd.js`; return acc; }, {}), 'jszip': 'npm:jszip@3.10.1/dist/jszip.min.js', 'tslib': 'npm:tslib@2.6.1/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', '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.8/standalone.js', 'prettier/parser-html': 'npm:prettier@2.8.8/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.13/inferno/package.json', 'npm:rxjs@7.5.3/package.json', 'npm:rxjs@7.5.3/operators/package.json', 'npm:devexpress-diagram@2.2.13/package.json', 'npm:devexpress-gantt@4.1.56/package.json', ], }; System.config(window.config); // System.import('@angular/compiler').catch(console.error.bind(console));
<!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/24.1.7/css/dx.light.css" /> <link rel="stylesheet" type="text/css" href="https://cdn3.devexpress.com/jslib/24.1.7/css/dx-gantt.css" /> <script src="https://unpkg.com/core-js@2.6.12/client/shim.min.js"></script> <script src="https://unpkg.com/zone.js@0.13.3/bundles/zone.umd.min.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>

Implementation: Use the taskContentTemplate template to customize and align task information within the template container. For each task, use the item property to obtain task information (title, resource, progress) and wrap it into div elements. Then, apply CSS styles to these div elements and place them into the container.