Our blazing fast Grid component built with pure JavaScript


Post by gokulnaths »

Dear Bryntum Support Team,

I’m reaching out for assistance with a recurring crash issue in our application using Bryntum Gantt. We’re running into stability problems under specific conditions with a large dataset, and despite our optimization attempts, we need your expertise to resolve this.

Our Setup
Bryntum Gantt Version: 6.0.4
Framework: Angular with Ng-Zorro components
Dataset: 10,000+ tasks, 35+ columns

One of the scenarios our Gantt crashes:

The user selects all 35+ columns using the “Select All” option in the nz-select.
Immediately scrolls the Gantt or attempts to update task values (e.g., editing a field).

Symptoms: The page freezes or crashes entirely (e.g., “Aw, Snap!” in Chrome).

Dataset Context: We must display all 10,000+ tasks without pagination, per end-user requirements.
Code Overview

constructor(private ngZone: NgZone) {
  this.debouncedColumnVisibility = _.debounce(() => {
    this.ngZone.runOutsideAngular(() => {
      if (!this.isGanttBusy) this.setColumnVisibility();
    });
  }, 200);
}

setColumnVisibility(): void {
    this.ganttInstance.columns.forEach(column => {
      if (this.columnsSelected.includes(column.text) || column.text == 'Time Axis') {
        column.hidden = false;
      } else {
        column.hidden = true
      }
    });
  }
  
this.ganttConfig = { dependencyIdField: 'wbsCode', // dependencyIdField: 'sequenceNumber', selectionMode: { cell: true, dragSelect: true, rowNumber: true }, preserveScrollOnDatasetChange: true, displaySchedulingIssueResolutionPopup: false, showTaskColorPickers: false, showTooltip: false, taskMenuFeature: { items: { cut: false, editTask: false, // add: { // menu: { // subtask: false, // milestone: false, // } // }, add: false, addTaskAbove: { text: 'Add Task above', icon: 'b-icon-up', }, addTaskBelow: { text: 'Add Task below', icon: 'b-icon-down', }, delete: true, convertToMilestone: false, linkTasks: false, unlinkTasks: false } }, projectLinesFeature: { disabled: true }, cellEditFeature: { addNewAtEnd: false }, cellTooltipFeature: { disabled: false, cls: 'custom-css-tooltip', hoverDelay: 100 }, taskTooltipFeature: { disabled: true // template: ({ taskRecord }) => `${taskRecord.name}`, // // Tooltip configs can be used here // align: 'l-r' // Align left to right }, project: { autoCalculatePercentDoneForParentTasks: false, skipNonWorkingTimeWhenSchedulingManually: true, skipNonWorkingTimeInDurationWhenSchedulingManually: true, autoSetConstraints: true, hoursPerDay: 23.999999999, daysPerWeek: 5, daysPerMonth: 20, calendar: 'general', calendarsData: this.simulationData?.calendars?.rows ? this.simulationData.calendars.rows : [ { id: "general", name: "General", intervals: [ { recurrentStartDate: "on Sat at 0:00", recurrentEndDate: "on Mon at 0:00", isWorking: false } ] } ], autoLoad: true, assignmentStore: { data: this.simulationData.assignments.rows, useRawData: true, }, resourceStore: { data: this.simulationData.resources.rows, useRawData: true, }, taskStore: { data: this.simulationData.tasks.rows, useRawData: true, wbsMode: 'auto' }, dependencyStore: { data: this.simulationData.dependencies.rows, useRawData: true, }, timeRangeStore: { data: [ { "id": 1, "name": moment().format('Do MMM YY'), "startDate": moment().format('YYYY-MM-DD'), "duration": 0, "durationUnit": "d", "cls": "b-fa b-fa-calendar-day" } ] }, listeners: { dataReady: ({ records }) => { console.log('Calculations finished'); this.displayGantt = true; this.message.remove(this.loadingIndicator); this.originalStartDate = this.ganttInstance.startDate; this.originalEndDate = this.ganttInstance.endDate; }, }, // The State TrackingManager, which the UndoRedo widget in the toolbar uses stm: { // NOTE, that this option does not enable the STM itself, this is done by the `undoredo` widget, defined in the toolbar // If you don't use `undoredo` widget in your app, you need to enable STM manually: `stm.enable()`, // otherwise, it won't be tracking changes in the data // It's usually best to enable STM after the initial data loading is completed. autoRecord: true }, // This config enables response validation and dumping of found errors to the browser console. // It's meant to be used as a development stage helper only, so please set it to false for production systems. validateResponse: true, delayCalculation: true, useRawData: true }, // startDate: '2019-01-12', // endDate: '2019-03-24', startDate: this.simulationData.project.startDate, endDate: this.simulationData.project.endDate, columns: [ // 35+ columns ], listeners: { cellClick: ({ grid, record, column, cellElement, target, event }: any) => { // console.log("Clicked on cell", grid, record, column, cellElement, target, event) } }, subGridConfigs: { locked: { width: 1000, collapsed: false }, }, columnLines: false, timeAxis: { useRawData: true }, scrollButtonsFeature: { disabled: false }, rollupsFeature: { disabled: true }, dependenciesFeature: { disabled: false, radius: 50, clickWidth: 50 }, dependencyEditFeature: { disabled: false, }, baselinesFeature: { disabled: true, renderer: ({ baselineRecord, taskRecord, renderData }: any) => { // console.log("Baseline record", baselineRecord) if (baselineRecord.isScheduled && baselineRecord.endDate.getTime() + 24 * 3600 * 1000 < taskRecord.endDate.getTime()) { renderData.className['b-baseline-behind'] = 1; } else if (taskRecord.endDate < baselineRecord.endDate) { renderData.className['b-baseline-ahead'] = 1; } else { renderData.className['b-baseline-on-time'] = 1; } }, }, progressLineFeature: { disabled: true, }, headerMenuFeature: false, filterFeature: false, sortFeature: false, columnReorderFeature: { stretchedDragProxy: true }, pdfExportFeature: { disabled: false }, timeRangesFeature: { showCurrentTimeLine: false, }, labelsFeature: { left: { field: 'name', editor: { type: 'textfield' } } }, bufferCoef: 1.5, // Tighter buffer visibleRows: 40, // Fewer visible rows // Add scroll/edit protection scrollable: { overflowX: 'scroll', overflowY: 'scroll', }, tbar: { // @ts-ignore This is an application custom widget type: 'gantttoolbar' } }
Last edited by gokulnaths on Wed Mar 19, 2025 9:29 am, edited 1 time in total.

Post by gokulnaths »

Dear Bryntum Support Team,

I’m reaching out for assistance with a recurring crash issue in our application using Bryntum Gantt. We’re running into stability problems under specific conditions with a large dataset, and despite our optimization attempts, we need your expertise to resolve this.

Our Setup
Bryntum Gantt Version: 6.0.4
Framework: Angular with Ng-Zorro components
Dataset: 10,000+ tasks, 35+ columns

One of the scenarios our Gantt crashes:

The user selects all 35+ columns using the “Select All” option in the nz-select.
Immediately scrolls the Gantt or attempts to update task values (e.g., editing a field).

Symptoms: The page freezes or crashes entirely (e.g., “Aw, Snap!” in Chrome).

Dataset Context: We must display all 10,000+ tasks without pagination, per end-user requirements.
Code Overview

constructor(private ngZone: NgZone) {
  this.debouncedColumnVisibility = _.debounce(() => {
    this.ngZone.runOutsideAngular(() => {
      if (!this.isGanttBusy) this.setColumnVisibility();
    });
  }, 200);
}

setColumnVisibility(): void {
    this.ganttInstance.columns.forEach(column => {
      if (this.columnsSelected.includes(column.text) || column.text == 'Time Axis') {
        column.hidden = false;
      } else {
        column.hidden = true
      }
    });
  }
  
this.ganttConfig = { dependencyIdField: 'wbsCode', // dependencyIdField: 'sequenceNumber', selectionMode: { cell: true, dragSelect: true, rowNumber: true }, preserveScrollOnDatasetChange: true, displaySchedulingIssueResolutionPopup: false, showTaskColorPickers: false, showTooltip: false, taskMenuFeature: { items: { cut: false, editTask: false, // add: { // menu: { // subtask: false, // milestone: false, // } // }, add: false, addTaskAbove: { text: 'Add Task above', icon: 'b-icon-up', }, addTaskBelow: { text: 'Add Task below', icon: 'b-icon-down', }, delete: true, convertToMilestone: false, linkTasks: false, unlinkTasks: false } }, projectLinesFeature: { disabled: true }, cellEditFeature: { addNewAtEnd: false }, cellTooltipFeature: { disabled: false, cls: 'custom-css-tooltip', hoverDelay: 100 }, taskTooltipFeature: { disabled: true }, project: { autoCalculatePercentDoneForParentTasks: false, skipNonWorkingTimeWhenSchedulingManually: true, skipNonWorkingTimeInDurationWhenSchedulingManually: true, autoSetConstraints: true, hoursPerDay: 23.999999999, daysPerWeek: 5, daysPerMonth: 20, calendar: 'general', autoLoad: true, assignmentStore: { data: this.simulationData.assignments.rows, useRawData: true, }, resourceStore: { data: this.simulationData.resources.rows, useRawData: true, }, taskStore: { data: this.simulationData.tasks.rows, useRawData: true, wbsMode: 'auto' }, dependencyStore: { data: this.simulationData.dependencies.rows, useRawData: true, }, timeRangeStore: { data: [ { "id": 1, "name": moment().format('Do MMM YY'), "startDate": moment().format('YYYY-MM-DD'), "duration": 0, "durationUnit": "d", "cls": "b-fa b-fa-calendar-day" } ] }, listeners: { dataReady: ({ records }) => { console.log('Calculations finished'); this.displayGantt = true; this.message.remove(this.loadingIndicator); this.originalStartDate = this.ganttInstance.startDate; this.originalEndDate = this.ganttInstance.endDate; }, }, // This config enables response validation and dumping of found errors to the browser console. // It's meant to be used as a development stage helper only, so please set it to false for production systems. validateResponse: true, delayCalculation: true, useRawData: true }, // startDate: '2019-01-12', // endDate: '2019-03-24', startDate: this.simulationData.project.startDate, endDate: this.simulationData.project.endDate, columns: [ // 35+ columns ], subGridConfigs: { locked: { width: 1000, collapsed: false }, }, columnLines: false, timeAxis: { useRawData: true }, scrollButtonsFeature: { disabled: false }, rollupsFeature: { disabled: true }, dependenciesFeature: { disabled: false, radius: 50, clickWidth: 50 }, dependencyEditFeature: { disabled: false, }, baselinesFeature: { disabled: true, renderer: ({ baselineRecord, taskRecord, renderData }: any) => { // console.log("Baseline record", baselineRecord) if (baselineRecord.isScheduled && baselineRecord.endDate.getTime() + 24 * 3600 * 1000 < taskRecord.endDate.getTime()) { renderData.className['b-baseline-behind'] = 1; } else if (taskRecord.endDate < baselineRecord.endDate) { renderData.className['b-baseline-ahead'] = 1; } else { renderData.className['b-baseline-on-time'] = 1; } }, }, progressLineFeature: { disabled: true, }, headerMenuFeature: false, filterFeature: false, sortFeature: false, columnReorderFeature: { stretchedDragProxy: true }, bufferCoef: 1.5, // Tighter buffer visibleRows: 40, // Fewer visible rows // Add scroll/edit protection scrollable: { overflowX: 'scroll', overflowY: 'scroll', }, tbar: { // @ts-ignore This is an application custom widget type: 'gantttoolbar' } }

Post by tasnim »

Hi gokulnaths,

Thanks for reaching out. Are you able to reproduce it here in our online demo https://bryntum.com/products/gantt/examples/bigdataset/?

If not, could you please share a reproducible test case with us where we can reproduce/debug the issue?

Best regards,
Tasnim

How to ask for help? Please read our Support Policy


Post by gokulnaths »

Hi tasnim,

Thanks for responding to our issue. In our case, the issue persists when there are more hidden columns initially and when we are trying to toggle all of them by triggering their hidden field set to true. Whereas initially, when the gantt initialises, we see no issue. Can you please refer to our Gantt config and help us here, please?

Thanks.


Post by mats »

Toggling column visibility like this will refresh the view excessively.

this.ganttInstance.columns.forEach(column => {
      if (this.columnsSelected.includes(column.text) || column.text == 'Time Axis') {
        column.hidden = false;
      } else {
        column.hidden = true
      }
    });

Better:

this.ganttInstance.columns.forEach(column => {
this.ganttInstance.suspendRefresh();
      if (this.columnsSelected.includes(column.text) || column.text == 'Time Axis') {
        column.hidden = false;
      } else {
        column.hidden = true
      }
      this.ganttInstance.resumeRefresh();

});

Does that fix it?


Post by gokulnaths »

Thanks for your support, mats; this helped us in bringing down the usage of RAM.

Appreciating your quick support.


Post Reply