Performance tips for EJ2 Angular Gantt control
2 Sep 202524 minutes to read
This guide offers tips to boost the Angular Gantt chart’s loading performance, especially for large datasets. It provides valuable insights into the steps required to bind a large data source without experiencing performance degradation. By offering detailed explanations and actionable tips, this resource aims to empower readers with the knowledge and best practices necessary to optimize the performance of the Angular Gantt component during data binding, ensuring a smooth and efficient user experience.
How to improve loading performance by binding large dataset
A Gantt chart consists of rows, columns, and taskbars. For example, binding 10 rows and 10 columns results in rendering 100 elements in the Document Object Model (DOM) of Grid area and 10 elements in the Document Object Model (DOM) of chart area. To ensure optimal loading performance for the component, it is recommended to limit the number of rows and columns rendered. This approach helps in efficiently managing large datasets and enhancing the overall user experience.
Optimizing performance with virtualization
To enhance your application’s efficiency, especially when dealing with substantial datasets, it is recommended to using virtualization. Implementing these techniques can significantly reduce the load on your application and elevate its overall performance.
-
Row Virtualization: The Virtual scrolling feature in the Angular Gantt component enables the efficient handling and display of large volumes of data without compromising performance. This approach optimizes the rendering process by loading only the visible rows within the Gantt viewport, rather than rendering the entire dataset simultaneously. For more information on implementing row virtualization , you can refer to the documentation section dedicated to this feature. This can be visualized while performing the vertical scroll action.
-
Timeline Virtualization: The timeline virtualization feature in the Angular Gantt Component enables efficient handling and display of large timespan without compromising performance. This approach optimizes the rendering process by loading only the visible timeline cells, which are typically three times the width of the Gantt element. Other timeline cells render on-demand during horizontal scrolling. For more information on implementing timeline virtualization, you can refer to the documentation section dedicated to this feature. This can be visualized while performing the horizontal scroll action.
-
Load On Demand: The Load on demand feature in the Angular Gantt component enables you to render a large number of tasks in the Gantt Chart with optimal performance. With virtualization enabled, only the root-level records are fetched from the datasource during the initial load. When expanding a root parent node or scrolling vertically, the corresponding tasks are dynamically fetched from the datasource and updated in the DOM based on the current viewport position. This ensures that only the necessary data is rendered, significantly improving performance and responsiveness.
Optimizing performance with AutoCalculateDateScheduling
In the Angular Gantt chart component, by default it automatically calculates the start and end dates in dataSource based on various factors such as working time, holidays, weekends, and predecessors. However, when rendering a large dataset, these calculations for data validation may result in performance issues. To avoid this, set the autoCalculateDateScheduling property to false.
The autoCalculateDateScheduling
property can help you reduce the time taken for the Gantt chart to render on the initial load. When this API is enabled, parent-child validation, data validation, and predecessor validation are restricted, allowing the Gantt chart to load more quickly. Since we are disabling the validations, data source provided to gantt should have all data such as start date, end date, duration, as proper data.
import { NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { GanttModule } from '@syncfusion/ej2-angular-gantt'
import { Component, ViewEncapsulation, OnInit } from '@angular/core';
import { GanttComponent, VirtualScrollService } from '@syncfusion/ej2-angular-gantt';
import { ToolbarItem, EditSettingsModel } from '@syncfusion/ej2-angular-gantt';
@Component({
imports: [
GanttModule
],
providers: [VirtualScrollService],
standalone: true,
selector: 'app-root',
template:
`<ejs-gantt id="ganttDefault" height="450px" [dataSource]="data" [taskFields]="taskSettings" [treeColumnIndex]="1"
[splitterSettings]="splitterSettings" [columns]="columns" [labelSettings]="labelSettings"
[allowSelection]="true" [enableVirtualization]="true" [autoCalculateDateScheduling]="false" [editSettings] = "editSettings" [highlightWeekends]="true"></ejs-gantt>`,
encapsulation: ViewEncapsulation.None
})
export class AppComponent{
// Data for Gantt
public data?: object[];
public taskSettings?: object;
public splitterSettings?: object;
public columns?: object[];
public editSettings?: EditSettingsModel;
public toolbar?: ToolbarItem[];
public labelSettings?: object;
public ngOnInit(): void {
let tempData: any[] = [
{
TaskID: 1, TaskName: 'Product concept',StartDate: new Date('04/02/2019'), EndDate: new Date('04/21/2019'),
parentID: 0
},
{
TaskID: 2, TaskName: 'Defining the product and its usage', StartDate: new Date('04/02/2019'),
Duration: 3, Progress: 30, parentID: 1
},
{
TaskID: 3, TaskName: 'Defining target audience', StartDate: new Date('04/02/2019'),
parentID: 1, Duration: 3
},
{
TaskID: 4, TaskName: 'Prepare product sketch and notes', StartDate: new Date('04/05/2019'),
Duration: 2, parentID: 1, Progress: 30
},
{
TaskID: 5, TaskName: 'Concept approval', StartDate: new Date('04/08/2019'),
parentID: 0, Duration: 0
},
{
TaskID: 6, TaskName: 'Market research', StartDate: new Date('04/02/2019'),
parentID: 0, EndDate: new Date('04/21/2019')
},
{
TaskID: 7, TaskName: 'Demand analysis', StartDate: new Date('04/04/2019'),
EndDate: new Date('04/21/2019'), parentID: 6
},
{
TaskID: 8, TaskName: 'Customer strength', StartDate: new Date('04/09/2019'),
Duration: 4, parentID: 7, Progress: 30
},
{
TaskID: 9, TaskName: 'Market opportunity analysis', StartDate: new Date('04/09/2019'),
Duration: 4, parentID: 7
},
{
TaskID: 10, TaskName: 'Competitor analysis', StartDate: new Date('04/15/2019'),
Duration: 4, parentID: 6, Progress: 30
},
{
TaskID: 11, TaskName: 'Product strength analsysis', StartDate: new Date('04/15/2019'),
Duration: 4, parentID: 6
},
{
TaskID: 12, TaskName: 'Research complete', StartDate: new Date('04/18/2019'),
Duration: 0, parentID: 6
},
{
TaskID: 13, TaskName: 'Product design and development', StartDate: new Date('04/04/2019'),
parentID: 0, EndDate: new Date('04/21/2019')
},
{
TaskID: 14, TaskName: 'Functionality design', StartDate: new Date('04/19/2019'),
Duration: 3, parentID: 13, Progress: 30
},
{
TaskID: 15, TaskName: 'Quality design', StartDate: new Date('04/19/2019'),
Duration: 3, parentID: 13
},
{
TaskID: 16, TaskName: 'Define reliability', StartDate: new Date('04/24/2019'),
Duration: 2, Progress: 30, parentID: 13
},
{
TaskID: 17, TaskName: 'Identifying raw materials', StartDate: new Date('04/24/2019'),
Duration: 2, parentID: 13
},
{
TaskID: 18, TaskName: 'Define cost plan', StartDate: new Date('04/04/2019'),
parentID: 13, EndDate: new Date('04/21/2019')
},
{
TaskID: 19, TaskName: 'Manufacturing cost', StartDate: new Date('04/26/2019'),
Duration: 2, Progress: 30, parentID: 18
},
{
TaskID: 20, TaskName: 'Selling cost', StartDate: new Date('04/26/2019'),
Duration: 2, parentID: 18
},
{
TaskID: 21, TaskName: 'Development of the final design', StartDate: new Date('04/30/2019'),
parentID: 13, EndDate: new Date('04/21/2019')
},
{
TaskID: 22, TaskName: 'Defining dimensions and package volume', StartDate: new Date('04/30/2019'),
Duration: 2, parentID: 21, Progress: 30
},
{
TaskID: 23, TaskName: 'Develop design to meet industry standards', StartDate: new Date('05/02/2019'),
Duration: 2, parentID: 21
},
{
TaskID: 24, TaskName: 'Include all the details', StartDate: new Date('05/06/2019'),
Duration: 3, parentID: 21
},
{
TaskID: 25, TaskName: 'CAD computer-aided design', StartDate: new Date('05/09/2019'),
Duration: 3, parentID: 13, Progress: 30
},
{
TaskID: 26, TaskName: 'CAM computer-aided manufacturing', StartDate: new Date('09/14/2019'),
Duration: 3, parentID: 13
},
{
TaskID: 27, TaskName: 'Design complete', StartDate: new Date('05/16/2019'),
Duration: 0, parentID: 13
},
{
TaskID: 28, TaskName: 'Prototype testing', StartDate: new Date('05/17/2019'),
Duration: 4, Progress: 30, parentID: 0
},
{
TaskID: 29, TaskName: 'Include feedback', StartDate: new Date('05/17/2019'),
Duration: 4, parentID: 0
},
{
TaskID: 30, TaskName: 'Manufacturing', StartDate: new Date('05/23/2019'),
Duration: 5, Progress: 30, parentID: 0
},
{
TaskID: 31, TaskName: 'Assembling materials to finsihed goods', StartDate: new Date('05/30/2019'),
Duration: 5, parentID: 0
},
{
TaskID: 32, TaskName: 'Feedback and testing', StartDate: new Date('04/04/2019'),
parentID: 0, EndDate: new Date('04/21/2019'),
},
{
TaskID: 33, TaskName: 'Internal testing and feedback', StartDate: new Date('06/06/2019'),
Duration: 3, parentID: 32, Progress: 45
},
{
TaskID: 34, TaskName: 'Customer testing and feedback', StartDate: new Date('06/11/2019'),
Duration: 3, parentID: 32, Progress: 50
},
{
TaskID: 35, TaskName: 'Final product development', StartDate: new Date('04/04/2019'),
parentID: 0, EndDate: new Date('04/21/2019'),
},
{
TaskID: 36, TaskName: 'Important improvements', StartDate: new Date('06/14/2019'),
Duration: 4, Progress: 30, parentID: 35
},
{
TaskID: 37, TaskName: 'Address any unforeseen issues', StartDate: new Date('06/14/2019'),
Duration: 4, Progress: 30, parentID: 35
},
{
TaskID: 38, TaskName: 'Final product', StartDate: new Date('04/04/2019'),
parentID: 0, EndDate: new Date('04/21/2019'),
},
{
TaskID: 39, TaskName: 'Branding product', StartDate: new Date('06/20/2019'),
Duration: 4, parentID: 38
},
{
TaskID: 40, TaskName: 'Marketing and presales', StartDate: new Date('06/26/2019'), Duration: 4,
Progress: 30, parentID: 38
}
];
let virtualData: any[] = [];
let projId: number = 1;
for (let i: number = 0; i < 50; i++) {
let x: number = virtualData.length + 1;
let parent: any = {};
/* tslint:disable:no-string-literal */
parent['TaskID'] = x;
parent['TaskName'] = 'Project ' + (i + 1);
virtualData.push(parent);
for (let j: number = 0; j < tempData.length; j++) {
let subtasks: any = {};
/* tslint:disable:no-string-literal */
subtasks['TaskID'] = tempData[j].TaskID + x;
subtasks['TaskName'] = tempData[j].TaskName;
subtasks['StartDate'] = tempData[j].StartDate;
subtasks['Duration'] = tempData[j].Duration;
subtasks['Progress'] = tempData[j].Progress;
subtasks['parentID'] = tempData[j].parentID + x;
virtualData.push(subtasks);
}
}
this.data = virtualData,
this.taskSettings = {
id: 'TaskID',
name: 'TaskName',
startDate: 'StartDate',
endDate: 'EndDate',
duration: 'Duration',
progress: 'Progress',
parentID: 'parentID'
};
this.columns = [
{ field: 'TaskID' },
{ field: 'TaskName' },
{ field: 'StartDate' },
{ field: 'Duration' },
{ field: 'Progress' }
];
this.splitterSettings = {
columnIndex: 2
};
this.editSettings = {
allowAdding: true,
allowEditing: true,
allowDeleting: true,
allowTaskbarEditing: true,
showDeleteConfirmDialog: true
},
this.toolbar = ['Add', 'Cancel', 'CollapseAll', 'Delete', 'Edit', 'ExpandAll', 'NextTimeSpan', 'PrevTimeSpan', 'Search', 'Update', 'Indent', 'Outdent']
this.labelSettings = {
leftLabel: 'TaskName',
taskLabel: 'Progress'
};
}
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));
When setting
autoCalculateDateScheduling
property to false, you must provide the valid data source; otherwise, the Gantt chart will render with invalid dates.
Optimizing loading performance by binding large data by showing custom text or element
When integrating image or template elements into a gantt column, it’s recommended to utilize the Column Template feature instead of customizing data through the rowDataBound or queryCellInfo events. These events are triggered for each row and cell rendering, which can introduce delays in the component’s rendering process. Moreover, rendering custom elements using these events may lead to the persistence of rendered elements, potentially causing longer rendering times over time. By opting for the column template feature, you can efficiently fulfill this requirement without experiencing rendering delays and ensure a more streamlined rendering process.
Optimizing loading performance by referring individual script and CSS
To enhance the performance of the Syncfusion® Gantt component during initial rendering and certain actions, it is recommended to download specific component scripts using CRG (Custom Resource Generator) for optimized project loading. By default, the ej2.min.js script file includes all Syncfusion® component scripts, which may lead to longer load times. Using CRG, you can selectively choose the components and their modules that your project requires. Subsequently, you can download only the necessary scripts and CSS, thereby improving loading times and optimizing resource utilization according to your project’s needs.
So to improve the performance of gantt during the initial rendering, suggested you to refer individual script and CSS.
Performance Benchmarks
The tables below illustrate typical load times for Gantt charts with various configurations, comparing non-virtualized and virtualized scenarios. Each row reflects the impact of adding only the specified feature to a default parent-child hierarchy on loading and interactivity performance.
Test environment
- Component Version: Syncfusion Angular Gantt 31.1.17
- Angular Version: 20.1.0
- Browser: Edge 138
- Operating System: Windows 11
- CPU: 12th Gen Intel® Core™ i5-1235U
- RAM: 16GB
Non-virtualized scenario (2,500 tasks)
Scenario | Load time (seconds) |
---|---|
Default hierarchy(Parent-Child) | 3.8 |
+ Predecessor | 5.4 |
+ Resources | 6.5 |
+ Split taskbars | 7.8 |
Virtualized scenario (25,000 tasks)
Scenario | Load time (seconds) |
---|---|
Default hierarchy(Parent-Child) | 2.1 |
+ Predecessor | 5.6 |
+ Resources | 6.2 |
+ Split taskbars | 6.8 |
How to optimize server-side data operations with adaptors
The Angular Gantt component provides support for various adaptors (OData, ODataV4, WebAPI, URL, etc.) to facilitate server-side data operations and CRUD functionalities. By utilizing these adaptors along with the DataManager
component, you can seamlessly bind remote data sources to the Gantt and execute actions. During data operations such as filtering and sorting, the corresponding action queries are generated according to the adaptor’s requirements. It is crucial to handle these actions on the application side and return the processed data back to the Gantt. Refer to the documentation for comprehensive details. It’s worth noting that for efficient data processing, the suggested order for returning processed data to the Gantt is as follows:
- Filtering
- Sorting
- Aggregates
How to avoid MaxJsonLength error while passing large amount of records
The Angular Gantt component operates on a client-server basis, meaning data is sent as a JSON object between the client and server. The reported issue occurs due to the serialization of a large JSON object. To resolve this, you need to increase the maximum length for serializing large JSON objects. This can be done by altering the MaxJsonLength property in your web.config file or at the point of deserialization.
Solution: 1
<configuration>
<system.web.extensions>
<scripting>
<webServices>
<jsonSerialization maxJsonLength="25000000"/>
</webServices>
</scripting>
</system.web.extensions>
</configuration>
Solution : 2
var serializer = new JavaScriptSerializer { MaxJsonLength = Int32.MaxValue };
Optimizing Angular app performance with multiple gantt and templates
The performance degradation issue is specifically linked to the Angular framework and is unrelated to the Syncfusion® Gantt.
When your application’s DOM is populated with a large number of items, performance issues arise due to continuous change detection (e.g., typing into an input continuously). For more information on common reasons for slowdowns in Angular apps, you can refer to the documentation link:
In Angular, there are two default change detection strategies available:
-
Default Change Detection:
Utilizes the defaultCheckAlways
strategy, where change detection is automatic until explicitly deactivated. For example, entering a value into a text box triggers continuous change detection for all template references, leading to the reported issue. -
OnPush Change Detection:
Adopts theCheckOnce
strategy, disabling automatic change detection until reactivated by setting the strategy to Default (CheckAlways). Enabling this strategy ensures that change detection triggers only for the input text box, rather than for all template references, overcoming the reported issue.
To address this, it’s recommended to implement the OnPush change detection strategy in your application. This can be achieved by using the following code snippet:
@Component({
selector: "app-root",
templateUrl: "app.component.html",
providers: [ReorderService],
changeDetection: ChangeDetectionStrategy.OnPush
})
Using the OnPush strategy may lead to child components not being updated when the input changes. You can address this by referring to the following links,
Microsoft Excel limitation while exporting millions of records to excel file format
By default, Microsoft Excel supports only 1,048,576 records per sheet. Therefore, exporting millions of records directly to Excel is not feasible. For more details on Microsoft Excel specifications and limits, you can refer to the documentation. It is recommended to export large datasets in CSV (Comma-Separated Values) or other formats that handle large data more efficiently than Excel.
Tree shaking and bundle size optimization
The Syncfusion® Angular Gantt component is designed with tree shaking as a primary feature. It’s organized into modular, tree-shakable packages, allowing you to import specific modules based on your requirements. For example, import the GanttModule
for basic functionality or include feature modules such as FilterService
, SortService
, or SelectionService
when needed:
import { GanttModule } from '@syncfusion/ej2-angular-gantt';
import { FilterService, SortService, SelectionService } from '@syncfusion/ej2-angular-gantt';
The table below shows example production build sizes. Each row demonstrates the effect of importing just the base Gantt or adding a specific service on top. Actual bundle sizes may vary based on your exact app, Angular version, and build optimizations.
Each row adds only the named feature/service to the base GanttModule
, demonstrating the incremental effect on your bundle.
Module | Raw Size | Transfer Size |
---|---|---|
Empty App | 220.26 kB | 60.71 kB |
GanttModule | 2.55 MB | 473.29 kB |
+ SortService | 2.56 MB | 475.69 kB |
+ SelectionService | 2.58 MB | 477.38 kB |
+ FilterService | 2.99 MB | 538.76 kB |
+ ReorderService | 3.00 MB | 541.35 kB |
+ ExcelService | 3.14 MB | 569.72 kB |
- Raw Size: The uncompressed size of the bundle.
- Transfer Size: The compressed size, which is what gets downloaded. Compressed sizes are crucial as they reduce the time and bandwidth required to transfer resources over the network, leading to faster application load times.
These numbers demonstrate how tree shaking keeps bundle sizes manageable by including only the features you import. For comparison, importing GanttAllModule
instead of selectively adding only needed modules will significantly increase your bundle size, as it includes all Gantt chart features whether or not you use them.
For detailed implementation steps and configuration, refer to the Syncfusion Angular Tree Shaking.