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'
}
}