Interactive Gantt Chart with AngularJS and dhtmlxGantt

| Comments (9)

Last time we explained how to use dhtmlxScheduler with AngularJS, a popular JavaScript MVW framework. In this tutorial, we will guide you through adding an interactive Gantt chart to an AngularJS app using dhtmlxGantt. dhtmlxGantt is an open source (GPL) JavaScript library that draws attractive Gantt charts and provides a convenient way of project schedule visualization.

download gantt

You can check the online demo, or just grab the final demo from GitHub.

Adding dhxGantt Directive

To build a custom HTML component in AngularJS, we need to create a new directive. So we start the integration of dhtmlxGantt from writing a directive for it. For that, we create a .js file with the following content:

app.directive('dhxGantt', function() {
  return {
    restrict: 'A',
    scope: false,
    transclude: true,
    template: '<div ng-transclude></div>',

    link:function ($scope, $element, $attrs, $controller){
      //size of gantt
      $scope.$watch(function() {
        return $element[0].offsetWidth + "." + $element[0].offsetHeight;
      }, function() {
        gantt.setSizes();
      });

      //init gantt
      gantt.init($element[0]);
    }
  }
});

This is what the above code does:

  • the code defines a new directive which can be used as a custom attribute
  • the new diretive will initialize dhtmlxGantt in the target container
  • the $watch function will check the size of dhtmlxGantt’s container and will resize the component when the size of the HTML container is changed

If we just need to create an instance of dhtmlxGantt on a page, the above code is enough. We just need to place the directive somewhere on the page. If we need to create a simple AngularJS app with a Gantt chart inside, we need the following HTML code structure:

<!doctype html>
<html lang="en" ng-app="ganttApp">
<head>
  <meta charset="utf-8">
  <title>AngularJS Gantt chart demo</title>

  <link rel="stylesheet" href="css/app.css">
  <link rel="stylesheet" href="lib/gantt/dhtmlxgantt.css">

  <script src="lib/angular/angular.min.js"></script>
  <script src="lib/scheduler/dhtmlxgantt.js"></script>
  <script src="js/app.js"></script>
</head>
<body>
    <div dhx-gantt style="height:350px; width:100%;"><div>
</body>
</html>

If you look at the code above, you’ll see that we included the .js and .css files of both the AngularJS framework and the dhtmlxGantt component. The dxhGantt directive, which we created earlier, is used in the code of the page. The dhx-gantt attribute is used to trigger the directive and to initialize the dhtmlxGantt component on the page.

Now, if you open a page with the described HTML code in a browser, you can see the Gantt chart but without any data yet:

dhtmlxGantt with AngularJS - Empty Gantt Chart

Linking to a Scope

At this stage the DHTMLX Gantt chart just renders itself on a page, which is good but not quite useful yet. To expand the functionality of our demo app, we now need to link the content of the Gantt chart to the scope. So we extend the link method of the directive like this:

    link:function ($scope, $element, $attrs, $controller){
      //watch data collection, reload on changes
      $scope.$watch($attrs.data, function(collection){
        gantt.clearAll();
        gantt.parse(collection, "json");
      }, true);

      //size of gantt
      $scope.$watch(function() {
        return $element[0].offsetWidth + "." + $element[0].offsetHeight;
      }, function() {
        gantt.setSizes();
      });

      //init gantt
      gantt.init($element[0]);
    }

The new block of code at the top will monitor the state of data collection and reload the Gantt chart with new data when collection is changed.

After applying such changes we can load data from the scope to the Gantt chart:

app.controller('MainSchedulerCtrl', function($scope) {
  $scope.tasks = {
    data:[
      {id:1, text:"Project #2", start_date:"01-04-2013", duration:18,order:10,
          progress:0.4, open: true},
      {id:2, text:"Task #1",    start_date:"02-04-2013", duration:8, order:10,
          progress:0.6, parent:1},
      {id:3, text:"Task #2",    start_date:"11-04-2013", duration:8, order:20,
          progress:0.6, parent:1}
    ],
    links:[
      { id:1, source:1, target:2, type:"1"},
      { id:2, source:2, target:3, type:"0"},
      { id:3, source:3, target:4, type:"0"},
      { id:4, source:2, target:5, type:"2"},
    ]};

});
    <div dhx-gantt data="tasks" style="height:350px; width:600px;">

Now we have a Gantt chart populated with data. The next step will be customizing the appearance of tasks in the chart.

Defining the Look of Tasks

It’s time to define the content and appearance of tasks in the Gantt chart. To specify the look of tasks, we will use dhtmlxGantt’s JavaScript API for templating. The following code will define one more directive which can be used to define the templates of our Gantt chart:

  function templateHelper($element){
    var template = $element[0].innerHTML;
    return template.replace(/[\r\n]/g,"").replace(/"/g, "\\\"").replace(/\{\{task\.([^\}]+)\}\}/g, function(match, prop){
      if (prop.indexOf("|") != -1){
        var parts = prop.split("|");
        return "\"+gantt.aFilter('"+(parts[1]).trim()+"')(task."+(parts[0]).trim()+")+\"";
      }
      return '"+task.'+prop+'+"';
    });
  }

  app.directive('ganttTemplate', ['$filter', function($filter){
    gantt.aFilter = $filter;

    return {
      restrict: 'AE',
      terminal:true,

      link:function($scope, $element, $attrs, $controller){
        var template =  Function('sd','ed','task', 'return "'+templateHelper($element)+'"');
        gantt.templates[$attrs.ganttTemplate] = template;
      }
    };
  }]);

This code defines the ganttTemplate directive. This directive can be used both as an attribute or as a custom tag. The content of the directive will be converted to JavaScript function and used as a template of the Gantt chart. To specify the template for the Gantt chart, you can use the syntax very similar to the native AngularJS templates. This syntax implies the use of the {{task.property}} expressions as placeholders in the template’s text.

For instance, we can add this peace of code on the page:

<div gantt-template="task_text">
  {{task.text | uppercase}} - <strong>{{task.duration}} days</strong>
</div>

If we now reload the page with our demo app, we should see the following:

dhtmlxGantt with AngularJS - Custom Template

The name of “gantt-template” attribute is a name of dhtmlxGantt’s template which needs to be defined. You can find the list of available templates in the dhtmlxGantt’s documentation.

NOTE: the template defined in such a way does not have the full functionality of a native AngularJS template. You should consider the following points:

  • You need to use the format of the type {{task.some}} to define the template. Instead of “some“, place the necessary property of task object. “task.” in our example is a fixed part that is used only by the Gantt chart to define the task’s property. It is not related to the current scope.
  • The scope’s variables can’t be used inside of the template.

Grid Configuration

One more thing which can be done is configuration of the grid in dhtmlxGantt. We can add one more directive (actually two directives) to configure the columns and data in them.

  app.directive('ganttColumn', ['$filter', function($filter){
    gantt.aFilter = $filter;

    return {
      restrict: 'AE',
      terminal:true,

      link:function($scope, $element, $attrs, $controller){
        var label  = $attrs.label || " ";
        var width  = $attrs.width || "*";
        var align  = $attrs.align || "left";

        var template =  Function('task', 'return "'+templateHelper($element)+'"');
        var config = { template:template, label:label, width:width, align:align };

        if (!gantt.config.columnsSet)
            gantt.config.columnsSet = gantt.config.columns = [];

        if (!gantt.config.columns.length)
          config.tree = true;
        gantt.config.columns.push(config);

      }
    };
  }]);

The above code is very similar to ganttTemplate directive. It takes attributes from the tag, converts the content of the tag to the template and creates a new column in the grid’s configuration.

In addition to data columns, dhtmlxGantt can have extra column with “add task” icon. To add such a column, we can create a separate directive:

app.directive('ganttColumnAdd', ['$filter', function($filter){
  return {
    restrict: 'AE',
    terminal:true,
    link:function(){
      gantt.config.columns.push({ width:45, name:"add" });
    }
  }
}]);

With the above JS code, we can configure the grid columns through HTML code like this:

  <div data="tasks" dhx-gantt style="height:300px; width:100%;">
    <div gantt-column label="Task">{{task.text | lowercase}}</div>
    <div gantt-column align="center" width="200"  label="Duration">{{task.duration}}</div>
    <div gantt-column-add width="45"></div>
  </div>

With such a configuration, the Gantt chart will look like this:

dhtmlxGantt with AngularJS - Custom Grid

Summing Up

Although AngularJS doesn’t provide rich user interface widgets, we can add such a widget with the help of some additional code. This tutorial showed how using the custom directives (dhxGantt, ganttTemplate, ganttColumn and ganttColumnAdd), you can easily add an editable Gantt chart on the page and customize its appearance.

The above described functionality of the Gantt chart library shows only the basic features. dhtmlxGantt has a rich API and lot of properties and events. It makes no sense to wrap all of them into AngularJS directives, as you can easily make any necessary calls from Angular controller’s code (find the details of the dhtmlxGantt API in the documentation).

With a bit of code, you can integrate dhtmlxGantt in an AngularJS app and get an interactive Gantt chart for project scheduling needs.

Comments

  1. anton fend April 2, 2014 at 4:41 pm

    hi, are there more angularjs directives available for all the dhtmlx products? F.e. the scheduler?

    Thanks

    Toni

  2. Siyad May 14, 2014 at 2:36 pm

    Hi,
    I have a problem with above code, to be precise with the very first directive mentioned “dhxGantt”. On further debug of the script gantt.setSize() line throws an exception due to ‘gantt’ is undefined.
    How i can fix this issue?

    • Alex May 16, 2014 at 3:34 pm

      Be sure that you have included the dhtmlxgantt.js on the page.

  3. Zaw Min Tun May 21, 2014 at 1:23 pm

    So if I have a requirement like below, is it easy enough to extend?
    For a task, we need to show two blocks (in some scenarios), first block, the palnned time-frame, second block, the proposed time-frame (because the planned time-frame is not possible any more due to some latency)

    • Ivan (DHTMLX team) May 21, 2014 at 3:42 pm

      For the general questions about dhtmlxGantt features, please use our forum (registration is free).

      • Zaw Min Tun May 22, 2014 at 6:29 am

        Thanks, Ivan, I’ll do so.

  4. Zaw Min Tun May 21, 2014 at 3:09 pm

    Is it also possible to make this 100% behave like Gantt rather than a Pert chart? E.g. so many tasks on the same row.

  5. David Karasek September 26, 2016 at 11:52 pm

    Can you provide what’s needed in the example for adding and saving data back to the model? The example here does not appear to interact with the listener (scope watch) when adding or saving. I traced this through the chrome debugger and these event handlers appear to be missing.

Leave a Reply