Gantt dynamic load on demand

Hello,

I came across one of the example

In this example the code is loading first 10 records and when the user scrolls down, it fetches again 10 more records and load it into UI.

Say, the records are generated from server side with more than 30000 complex records dataset. This includes different type of dependencies, different types of milestones, multiple life cycles, resources and other things. We should first get N records only from server side and load the UI. When the user scrolls down, there will be another server call to fetch N number of records and show all N+N records in UI.
Now, the sample does not have any dependency been drawn from say task 1 till task 200 which is not in the current viewport.

  1. How will this behave or will the dependencies be shown in UI or will it skip drawing of dependency?
  2. Once the user reaches the end of the dataset, how will the UI respond with having large number of complex dataset in UI? Will there be any performance issue by having all 30K complex records?

Looking forward for your answer.

Thanks,
Surbhi

Hello @Surbhi_Agarwal ,

Thanks for reaching out!

How will this behave, or will the dependencies be shown in UI, or will it skip drawing of dependency?

DHTMLX Gantt displays dependency lines only when both linked tasks are loaded into the chart and rendered in the DOM. If Task 1 (initial chunk) depends on Task 200 (later chunk), the dependency line will appear only after Task 200 is fetched and rendered during scrolling.
You can check it in the following snippet: DHTMLX Snippet Tool.

Once the user reaches the end of the dataset, how will the UI respond with a large number of complex dataset in UI? Will there be any performance issue by having all 30K complex records?

DHTMLX Gantt is optimized for large datasets using a combination of techniques:

  • Smart Rendering. Only visible tasks and links are rendered, improving performance with big data volumes. This mode is enabled by default since version 6.2
  • Besides, it’s recommended to use the following techniques to improve the performance:
    • Set smart_scales: true. Only the visible portion of the time scale will be rendered on-screen. This optimization has been available since version 4.1 and improves responsiveness for long time spans.
    • Enable the dynamic loading (set the branch_loading option to ‘true’).
    • Increase the scale’s step (e.g., month/year).
    • Hide progress bars if not required (show_progress: false).
    • Predefine work time calendars before data load.
    • If using duration_unit as “hour” or “minute”, set duration_step = 1 for optimal work-time performance.

For more information on ways to improve performance, please check this article:
https://docs.dhtmlx.com/gantt/desktop__performance.html
Here is a sample with 30000 tasks and 3671 links loaded within ~1 sec: Gantt : Samples.

Please check also this sample demonstrating various performance tweaks: Gantt : Samples.

You can also read the following blog post about the results of DHTMLX Gantt 7.1.12 performance testing. These insights show that a Gantt chart can still maintain acceptable performance even with 30,000 complex records, depending on the feature set in use.

Best regards,
Valeria Ivashkevich
DHTMLX Support Engineer

Thanks Valeria,

I understand because of performance the dependency lines are not shown until source and target are rendered on UI. But, is there any way we can customize this behaviour to draw the hanging line from source until the target task is rendered completely in UI?
If so, can you please provide an example?

Thanks,
Surbhi

Hello @Surbhi_Agarwal,

Yes — you can try to achieve the desired behavior by implementing placeholder links from the source task to a provisional position until the target task is fully loaded. Once the target is rendered, Gantt’s native link rendering will take over automatically.

This can be done using the addTaskLayer() method. Below is an outline of this approach (full working example at the end):

  1. Attach the addTaskLayer method that processes tasks with outgoing links. In the render(task) function:
  • Skip tasks with no outgoing links (if (!task.$source.length) return false;)
  • Create a container element (mainEl) for the placeholder lines.
  • Call drawLinesForTask(task, mainEl) to render SVG lines when a link’s target is not present in the chart.
gantt.addTaskLayer({
  renderer: {
    render: function (task) {
      if (!task.$source.length) return false;
      const mainEl = document.createElement("div");
      mainEl.className = "custom_link_render";
      mainEl.setAttribute("task-id", task.id);
      drawLinesForTask(task, mainEl);
      return mainEl;
    },
    // ...
  }
});
  1. The drawLinesForTask() function iterates over each source link, and if the target doesn’t exist, calculates coordinates reaching down to the last visible task row:
if (!gantt.isTaskExists(link.target)) {
  const visibleTasks = gantt.getVisibleTaskCount();
  const lastVisibleTask = gantt.getTaskByIndex(visibleTasks - 1);
  
  const x = [gantt.posFromDate(task.end_date), gantt.posFromDate(task.end_date)];
  const y = [
    gantt.getTaskPosition(task).top + 16,
    gantt.getTaskPosition(lastVisibleTask).top + gantt.config.row_height / 2
  ];
  // ... build SVG line from (x[0], y[0]) to (x[1], y[1])
}

Use link.type to determine correct positioning:

switch (link.type) {
  case "0": // Finish-to-Start
    x[1] -= 5;
    break;
  case "1": // Start-to-Start
    x[0] = gantt.posFromDate(task.start_date);
    x[1] -= 5;
    break;
  case "2": // Finish-to-Finish
    x[1] = gantt.posFromDate(last.end_date) + 5;
    break;
  case "3": // Start-to-Finish
    x[0] = gantt.posFromDate(task.start_date);
    x[1] = gantt.posFromDate(last.end_date) + 5;
    break;
}

And then render SVG path:

const line = document.createElementNS("http://www.w3.org/2000/svg", "svg");
line.style.left = x[0] + "px";
line.style.top = y[0] + "px";
line.setAttribute("width", x[1] - x[0] + 2);
line.setAttribute("height", y[1] - y[0] + 10);
const path = document.createElementNS(line.namespaceURI, "path");
path.setAttribute("d", `M ${p1x} ${p1y} L ${p2x} ${p2y}`);
path.setAttribute("stroke-width", "5");
path.setAttribute("fill", "none");
line.appendChild(path);
mainEl.appendChild(line);
  1. Define getRectangle(task) so that Gantt knows where the custom element might appear, even off-screen:
getRectangle: function (task) {
  const visibleTasks = gantt.getVisibleTaskCount();
  const lastVisibleTask = gantt.getTaskByIndex(visibleTasks - 1);
  const pos = gantt.getTaskPosition(task);

  return {
    left: pos.left,
    top: pos.top,
    width: gantt.$task.offsetWidth,
    height: lastVisibleTask.top - pos.top
  };
}

Once the target is parsed into Gantt, your custom layer does not render the line for that link—Gantt’s built-in rendering displays it. This keeps visual consistency across all links.

Please check the full working example: DHTMLX Snippet Tool.

Best regards,
Valeria Ivashkevich
DHTMLX Support Engineer