Start Building Professional
Web Apps Today


Creating Event Calendar with dhtmlxScheduler and AngularJS

November 15th, 2013

AngularJS is a modern JavaScript framework that allows creating rich client-side apps in a fast and productive way. It goes without saying that AngularJS is convenient and powerful in working with HTML. However, it doesn’t have ready to use complex UI components yet. In this article we’ll try to partially eliminate this drawback by showing how to add support of dhtmlxScheduler, a Google-like event calendar, for an AngularJS app.

 
AngularJs Event Calendar with dhtmlxScheduler

 
You can check the online demo of a ready app or just grab the sources of the app from GitHub.

Adding dhxScheduler Directive

 
In order to build a custom HTML component in AngularJS we need to create a new directive. So let’s start integrating the Scheduler from writing a directive for it. For this purpose we need to create a .js file with the following content:

app.directive('dhxScheduler', function() {
  return {
    restrict: 'A',
    scope: false,
    transclude: true,
    template:'<div class="dhx_cal_navline" ng-transclude></div><div class="dhx_cal_header"></div><div class="dhx_cal_data"></div>',

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

      //styling for dhtmlx scheduler
      $element.addClass("dhx_cal_container");

      //init scheduler
      scheduler.init($element[0], new Date(), "month");
    }
  }

The above code may look a bit complicated, but the things the code produces are rather simple:

  • the code defines a new directive which can be used as a custom attribute
  • when the new directive (dhxScheduler) is initialized, it will create the dhtmlxScheduler object with the default settings
  • the watcher function will be constantly checking the size of scheduler’s container and will resize the component when the size of HTML container changes.


For creating an instance of dhtmlxScheduler on a page it’s actually enough to write the above code and place the directive somewhere on the page. If we want to create a simple app with a scheduler inside, the HTML code structure shown below is needed:

<!doctype html>
<html lang="en" ng-app="schedulerApp">
<head>
  <meta charset="utf-8">
  <title>Angular Calendar demo</title>

  <link rel="stylesheet" href="css/app.css">
  <link rel="stylesheet" href="lib/scheduler/dhtmlxscheduler.css">

  <script src="lib/angular/angular.min.js"></script>
  <script src="lib/scheduler/dhtmlxscheduler.js"></script>
  <script src="js/app.js"></script>
</head>
<body>
    <div dhx-scheduler style="height:350px; width:600px;">
        <div class="dhx_cal_prev_button">&nbsp;</div>
        <div class="dhx_cal_next_button">&nbsp;</div>
        <div class="dhx_cal_today_button"></div>
        <div class="dhx_cal_date"></div>
        <div class="dhx_cal_tab" name="day_tab" style="right:204px;"></div>
        <div class="dhx_cal_tab" name="week_tab" style="right:140px;"></div>
        <div class="dhx_cal_tab" name="month_tab" style="right:76px;"></div>    
    <div>
</body>
</html>

As you can see, we’ve included the .js and .css files of both the AngularJS framework and the dhtmlxScheduler component. Our newly created dxhScheduler directive is used in the code of the page. The dhx-scheduler attribute is used to trigger the directive and to initialize the scheduler component. The HTML tags inside of dhx-scheduler div container are necessary to define the buttons on the top bar of the scheduler.

When you open a page with the described HTML code in a browser, you will see an event calendar component ready to be used:

 
dhtmlxScheduler - Using with AngularJS
 

Linking to the Scope

 
For now our AngularJS event calendar just renders itself, which is good but not quite useful yet. Let’s link the scheduler’s state and content to the scope in order to expand the app’s functionality. For this purpose we need to extend the link method of the directive as follows:

link:function ($scope, $element, $attrs, $controller){
  //default state of the scheduler
  if (!$scope.scheduler)
    $scope.scheduler = {};
  $scope.scheduler.mode = $scope.scheduler.mode || "month";
  $scope.scheduler.date = $scope.scheduler.date || new Date();

  //watch data collection, reload on changes
  $scope.$watch($attrs.data, function(collection){
    scheduler.clearAll();
    scheduler.parse(collection, "json");
  }, true);

  //watch mode and date
  $scope.$watch(function(){
    return $scope.scheduler.mode + $scope.scheduler.date.toString();
  }, function(nv, ov) {
    var mode = scheduler.getState();
    if (nv.date != mode.date || nv.mode != mode.mode)
      scheduler.setCurrentView($scope.scheduler.date, $scope.scheduler.mode);
  }, true);

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

  //styling for dhtmlx scheduler
  $element.addClass("dhx_cal_container");

  //init scheduler
  scheduler.init($element[0], $scope.scheduler.date, $scope.scheduler.mode);
}

Let’s see what was added. The top block checks if there are settings for the scheduler in the scope. If there are no such settings, they will be created with default values.

After the top block, two new watch instructions follow. The first instruction will monitor the state of data collection and reload the scheduler with new data when the collection is changed. The second instruction will change the scheduler’s settings (mode and date) when the related parameters are changed in the scope.

Finally, we have slightly adjusted the scheduler.init command. Now it uses values from the scope as initial view and date values.

After applying all these changes we can do the following:

  • We can place custom buttons on the page. Clicking the first button will change the displayed date and clicking the second button will switch the mode of events view in the calendar:
     

        <button ng-click="scheduler.mode = 'week'">Week</button>
        <button ng-click="scheduler.date = new Date()">Week</button>
  • We can load data from the scope to the scheduler:
     

    app.controller('MainSchedulerCtrl', function($scope) {
      $scope.events = [
        { id:1, text:"Task A-12458",
          start_date: new Date(2013, 09, 30, 9, 0),
          end_date: new Date(2013, 09, 30, 16, 0) },
        { id:2, text:"Task A-83473",
          start_date: new Date(2013, 09, 28, 9, 0),
          end_date: new Date(2013, 09, 30, 16, 0) }
      ];

    });

       <div dhx-scheduler data="events" style="height:350px; width:600px;">
  • We can use some input on the page to filter the scheduler:
     

       <div dhx-scheduler data="events | filter:search" style="height:350px; width:600px;">

 
Thus we have a scheduler populated with data. The scheduler has the functions of date and mode changing, and data filtering. Next we can customize the appearance of events in the scheduler.

The Look of Events

 
Well, we can now define the content and appearance of event bars in the calendar. dhtmlxScheduler provides JavaScript API for templating and we can take the advantage of this API to specify the look of events in our calendar. The code below will define one more directive which can be used to define the scheduler’s templates:

app.directive('dhxTemplate', ['$filter', function($filter){
  scheduler.aFilter = $filter;

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

    link:function($scope, $element, $attrs, $controller){
      $element[0].style.display = 'none';

      var template = $element[0].innerHTML;
      template = template.replace(/[\r\n]/g,"").replace(/"/g, "\\\"").replace(/\{\{event\.([^\}]+)\}\}/g, function(match, prop){
        if (prop.indexOf("|") != -1){
          var parts = prop.split("|");
          return "\"+scheduler.aFilter('"+(parts[1]).trim()+"')(event."+(parts[0]).trim()+")+\"";
        }
        return '"+event.'+prop+'+"';
      });
      var templateFunc = Function('sd','ed','event', 'return "'+template+'"');
      scheduler.templates[$attrs.dhxTemplate] = templateFunc;
    }
  };
}]);

This code defines the dhxTemplate 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 scheduler. To specify the template for the scheduler, you can use the syntax very similar to that of the native AngularJS templates. This syntax implies the use of the {{event.property}} expressions as placeholders in the template’s text.

For example, we can add the following code on the page:

<div dhx-template="event_bar_text">
   <strong>#{{event.id}}</strong>: {{event.text | uppercase}}
</div>

The above code will produce the result as follows:

 
dhtmlxScheduler and AngularJS - Custom Template
 

The dhx-template attribute’s name (“event_bar_text” ) is the name of scheduler’s template which needs to be defined. Check the full list of templates in the documentation.

Please notice that the template defined in such a way won’t have the full functionality of a native AngularJS template. Take into account the following points:

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

 

Summing Up

 
So although there are no complex UI components in Angular JS, we can add such a component with the help of some additional code. Thus, by using the two directives defined above (dhxScheduler and dhxTemplate) you can easily add a rich-featured event calendar on the page and customize its appearance.

The above described functionality of the scheduler component is just a tip of the iceberg. dhtmlxScheduler 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 (check the details of the dhtmlxScheduler API in the documentation).

The simplicity of integration shows the flexibility of both AngularJS and dhtmlxScheduler. While both of them are independent solutions, with a bit of glue code they work together in a neat way. By using the above code you can get a Google-like event calendar for your AngularJS web app.

Comments

  1. Iris,
    January 12, 2014 at 5:05 pm

    Hello!

    Really nice example, but could you maybe give some pointer on how to load events from the database? I can not seem to find anything about this..

    Iris

    • irth orbits,
      February 5, 2014 at 1:34 am

      Great tut! Timely for me, can’t wait to try it.

      @Iris, you might create a service object called events, events could then be injected into your view’s controller function as an argument and then you would expose them via /* $scope.events = events */ to the calendar. Alternatively, you might use resolve from your routing to dump the event data. I think the Angular docs will do a better job of explaining your many options for different database hookups. I really love Firebase IO ;).

  2. Kevin,
    March 27, 2014 at 10:42 am

    Great blog.
    Can you make an example for timeline view scheduler? it would be a great help for me. thanks

    Kevin

  3. laxman,
    April 16, 2014 at 12:40 pm

    hi
    the scheduler control is nice,but could you give some example on how to restrict the event section update in the scheduler

    • Ivan,
      April 17, 2014 at 2:42 pm

      Sorry, I’m not quite sure what do you mean by “restrict section update”. Still, you can try to add something like:

      scheduler.config.readonly = true;

      in the js code, after scheduler.init() call.
      If you need more precise control, check the next article:
      http://docs.dhtmlx.com/scheduler/readonly.html

  4. Tuan,
    April 16, 2014 at 1:01 pm

    hi, I added the callback for onAfterTaskUpdate event such as

    gantt.attachEvent(“onAfterTaskUpdate”, function(id,item,e){
    console.log(“onAfterTaskUpdate”);
    $scope.$apply(function(){
    //do somecode
    });
    });

    but after I update the task, the grant autotmaticed scroll to top, how to fix this ?

    • Inga K.,
      April 17, 2014 at 2:36 pm

      Is it possible that in “do somecode” you are modifying the main collection that stores all tasks ?

      When collection is modified, dhtmlxGantt will repaint all events which can cause the scroll state change.

      • Tuan,
        April 21, 2014 at 5:24 am

        No. I just need wrote:
        $scope.$apply(function(){
        //nothing here
        });
        But the page also auto scroll to top

  5. dineshujjwal,
    June 14, 2014 at 6:33 am

    hello dear sir can you make date wise calender filter such as when user click on any date than in side panel related to that date all data will be filter …. thanx

    • Ivan (DHTMLX team),
      June 17, 2014 at 4:45 pm

      There is an API for a client side filtering, which is described here:
      http://docs.dhtmlx.com/scheduler/filtering.html

      So you’ll need to watch a selected date and redefine filter function accordingly.

      Please post your further technical questions to our free forum.

  6. Yogesh,
    June 16, 2014 at 4:08 pm

    I am getting this error on console :
    TypeError: Cannot read property ’0′ of undefined
    at Object.scheduler.render_view_data (file:///E:/yogesh/salesforce/Angular.js/Calender/angular-scheduler-demo-master/lib/scheduler/dhtmlxscheduler.js:157:454)
    at Object.scheduler.update_view (file:///E:/yogesh/salesforce/Angular.js/Calender/angular-scheduler-demo-master/lib/scheduler/dhtmlxscheduler.js:110:196)
    [..]

    Can you please help me ..

    • Stan,
      June 18, 2014 at 1:49 pm

      Be sure that “data” attribute contains a reference to valid data collection ( array of objects ) in the scope. The above error does look similar to the case, when “data” points to not-existing object.

  7. nicolas,
    June 19, 2014 at 2:24 am

    Hello
    In the init part of the app.scheduler.js file, i’m trying to change the color for my events :
    //init scheduler
    scheduler.config.displayed_event_color=”#DFEDF7″;
    scheduler.config.max_month_events = 5;
    scheduler.init($element[0], $scope.scheduler.mode, $scope.scheduler.date);

    I only have multi day event.

    Any idea why this is not working ?
    thanks,
    nicolas

    • Stan,
      June 19, 2014 at 3:41 pm

      This option will affect only results of showEvent call. It will not change the default color of event bars. You can do it through css. Something like next:

      .dhx_cal_event_line{ background-color: silver; color:white; }

      • nicolas,
        June 20, 2014 at 12:23 am

        it works fine, thanks !
        I just ahve to figured out how to have one different color per event type .

  8. nicolas,
    June 20, 2014 at 4:55 am

    Sorry to bother you with another question, but how can i change the save/delete button of the lightbox ?
    (i need the data to go to a db)
    thanks

  9. Dhairya,
    July 6, 2014 at 9:22 am

    getting exception at line “scheduler.init($element[0], $scope.scheduler.mode, $scope.scheduler.date);”. Anybody has some idea?

    • Dhairya,
      July 6, 2014 at 9:56 am

      got it! please fix the order of init method FROM
      “scheduler.init($element[0], $scope.scheduler.mode, $scope.scheduler.date);” TO “scheduler.init($element[0], $scope.scheduler.date, $scope.scheduler.mode);”. Also please end properly at line no. 29

      • Stanislav,
        July 14, 2014 at 7:39 pm

        I have fixed the order of parameters, thanks for the remark.
        Which line do mean by line #29 ?

  10. Deepak,
    August 9, 2014 at 8:51 am

    Hi, Nice article, wanted to try bootstrap along with it. will it work?

    • Ivan (DHTMLX team),
      August 11, 2014 at 1:24 pm

      Yes, it should work. But we recommend you to use the latest version of dhtmlxScheduler (4.1) if you want to use it with bootstrap.

  11. Kurt,
    October 15, 2014 at 3:02 am

    Hi, I am trying to use the scheduler with AngularJS and Firebase. I followed your example and read the comments above. I am getting data back from Firebase but I can’t get it to load into thhe scheduler. Do you have any examples for “real-time” data? Do you think you may have a connector like your Backbone?

    • Kurt,
      October 16, 2014 at 6:22 pm

      Hi, I figured it out.

  12. Scott,
    November 6, 2014 at 12:07 am

    Is it possible to use this example with UI Router? I’ve hooked up a clickEvent to display/edit event details in another route (an input form) which works great. However when I return to the route containing the scheduler it fails to reinitialize due to a pair of exceptions:

    - Cannot read property ‘replace’ of undefined at scheduler.date.date_to_str (http://workshop.dfc.dev/events/lib/scheduler/dhtmlxscheduler.js:134:365)

    - Cannot read property ‘rows’ of null at Object.scheduler._pre_render_events (http://workshop.dfc.dev/events/lib/scheduler/dhtmlxscheduler.js:162:320)

    Any advice or pointers would be most appreciated

    • Scott,
      November 6, 2014 at 1:00 am

      Apologies for the noise; still needs further testing but it looks like I was able to resolve this by taking a deep clone of window.scheduler on first run and then replacing the original window.scheduler with a deep clone of the uninitialized copy when returning to my route with the scheduler.

Leave a Reply