Start Building Professional
Web Apps Today


Creating Online Event Calendar with Node.js and dhtmlxScheduler

September 6th, 2013

The combination of Node.js and MongoDB is a useful tool for quick development of dynamic sites. In this tutorial, we will explain how dhtmlxScheduler, an embeddable JavaScript calendar, can be used to create an online event calendar with Node.js backend.

dhtmlxScheduler with Node.js - Event Calendar

You can download the full archive with the final solution from GitHub.

Creating a Simple Node.js Site

 
One of the advantages of Node.js is a rich library of ready to use solutions. That’s why we won’t write everything from scratch but will use ready modules: Express, as a web-framework, and MongoSkin, to work with MongoDB.

To start, let’s create a new directory, for example, ‘scheduler-node’, and install the necessary libraries into it by running the next command:

mkdir scheduler-node
cd scheduler-node
npm install express
npm install mongoskin

Then we create the base of our application – app.js file in the ‘scheduler-node’ directory – with the following content:

var express = require('express');
var path = require('path');

//connect to the mongoDB
var db = require('mongoskin').db("localhost/testdb", { w: 0});
    db.bind('event');

//create express app, use public folder for static files
var app = express();
app.use(express.static(path.join(__dirname, 'public')));

//is necessary for parsing POST request
app.use(express.bodyParser());

app.listen(3000);

Due to the use of the Express framework, the whole app fitted into several lines. It doesn’t execute anything extraordinary and just serves static files from the ‘public’ directory.

Let’s create ‘public’ directory and unpack the folder ‘codebase’ from the dhtmlxScheduler package into it (download dhtmlxScheduler Standard Edition). After that, the whole file structure will look like this:

dhtmlxScheduler with Node.js - File structure

Now, we create index.html file in the ‘public’ directory. It will be the main page where we will place our calendar. Here is the content of this file:

<!doctype html>
<head>
    <meta http-equiv="Content-type" content="text/html; charset=utf-8">
    <script src="codebase/dhtmlxscheduler.js" type="text/javascript" charset="utf-8"></script>
    <link rel="stylesheet" href="codebase/dhtmlxscheduler.css" type="text/css" media="screen" title="no title" charset="utf-8">
    <style type="text/css" media="screen">
        html, body{
            margin:0px;
            padding:0px;
            height:100%;
            overflow:hidden;
        }  
    </style>
</head>


<script type="text/javascript" charset="utf-8">
    function init() {
        scheduler.init('scheduler_here',new Date(2013,8,4),"month");
    }
</script>

<body onload="init();">
    <div id="scheduler_here" class="dhx_cal_container" style='width:100%; height:100%;'>
        <div class="dhx_cal_navline">
            <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>
        <div class="dhx_cal_header">
        </div>
        <div class="dhx_cal_data">
        </div>
    </div>
</body>

This is the basic HTML code needed for working with dhtmlxScheduler.

To check how the app works, we may start the Node.js server and open http://localhost:3000 address in a browser. If everything is done correctly, we will see a calendar without events (to start Node.js server, run “nodejs app.js” command).

dhtmlxScheduler with Node.js - Empty calendar

Loading Events

 
First, we add two new handlers into app.js:

app.get('/init', function(req, res){
    db.event.insert({
        text:"My test event A",
        start_date: new Date(2013,8,1),
        end_date:   new Date(2013,8,5)
    });
    db.event.insert({
        text:"One more test event",
        start_date: new Date(2013,8,3),
        end_date:   new Date(2013,8,8),
        color: "#DD8616"
    });

    /*... skipping similar code for other test events...*/

    res.send("Test events were added to the database")
});


app.get('/data', function(req, res){
    db.event.find().toArray(function(err, data){
        //set id property for all records
        for (var i = 0; i < data.length; i++)
            data[i].id = data[i]._id;

        //output response
        res.send(data);
    });
});

The first “/init” is necessary to generate test data. It just adds a few records into the database.

The second handler – “/data” – will be used to load data into the calendar. Here we choose all the records from the database and send them to the client side as JSON structure.

NOTE: Before sending the records, we define the id property for each object. It’s needed because dhtmlxScheduler expects that record id is stored in obj.id but MongoDB keeps this value in obj._id.

We also need to add some changes in index.html:

        scheduler.init('scheduler_here',new Date(2013,8,4),"month");

        scheduler.templates.xml_date = function(value){ return new Date(value); };
        scheduler.load("/data", "json");

We have added two lines. The first one defines how to parse dates from the incoming data. Most often, while loading data from the server, dates are stored like strings in a special format. In our case, dates are stored as timestamps and we can convert them into date objects through Date constructor. The second line initiates data loading, using the URL described above.

If we restart the server and open in browser firstly http://localhost:3000/init – to generate test data, and then http://localhost:3000, we will see our calendar with the events inside.

Adding, Editing, and Deleting Events

 
To add the ability to add, edit, and delete events in the calendar, we need to add the code that initializes dataprocessor to the index.html file:

        scheduler.config.xml_date="%Y-%m-%d %H:%i";

        var dp = new dataProcessor("/data");
        dp.init(scheduler);
        dp.setTransactionMode("POST", false);

The first line defines the format the dates will take while going back to the server. We are using default year-month-day order that can be parsed by MongoDB. The following block initializes dataprocessor and switches it to the mode of simple POST sending. From now on, each time when the data in the scheduler changes, dataprocessor will call “/data” url and will pass all the properties of the changed event.

Let’s now go over to the server code and add one more handler to app.js:

app.post('/data', function(req, res){
    var data = req.body;

    //get operation type
    var mode = data["!nativeeditor_status"];
    //get id of record
    var sid = data.id;
    var tid = sid;

    //remove properties which we do not want to save in DB
    delete data.id;
    delete data.gr_id;
    delete data["!nativeeditor_status"];


    //output confirmation response
    function update_response(err, result){
        if (err)
            mode = "error";
        else if (mode == "inserted")
            tid = data._id;

        res.setHeader("Content-Type","text/xml");
        res.send("<data><action type='"+mode+"' sid='"+sid+"' tid='"+tid+"'/></data>");
    }

    //run db operation
    if (mode == "updated")
        db.event.updateById( sid, data, update_response);
    else if (mode == "inserted")
        db.event.insert(data, update_response);
    else if (mode == "deleted")
        db.event.removeById( sid, update_response);
    else
        res.send("Not supported operation");
});

Here we use app.post, as all saving operations use POST queries. The first part of the code defines the type of operation and removes all the technical parameters from an incoming query to get an object that can be saved in the database.

Next, depending on the type of operation, we call the related method of db.event for adding/saving/deleting data. As the operations with database are asynchronous, we pass the specified earlier callback function update_response as the last parameter.

The client side expects to get the confirmation of saving operation or an error message – this is what the update_response function is responsible for. It generates an XML response in the necessary format and sends it to the client side. (Details of the format can be found in the documentation). In case of adding a new record, this function also includes ID generated by MongoDB for the new record, in response to update of the element id on the client side.

If we restart the server and open http://localhost:3000 in browser again, we’ll get a calendar with events that we can create, edit and delete. What is more, all the changes are automatically saved in the database and will be available after reloading the page.

Again, the final demo is available on GitHub so you can download it and view the details.

Final Thoughts

 
The server solution we’ve got is multipurpose. We can add any number of extra fields into the calendar, and we don’t need to change anything in the server code, as it will save and load new fields automatically. The code of data loading and data saving is quite simple. It won’t be difficult to add validation or to format data before loading, if needed.

By slightly expanding this code, we may get a Node.js event calendar that allows several users to edit the calendar simultaneously (update the data on the client side without reloading pages). However, it is a topic for a separate article.

Comments

  1. Gor,
    February 25, 2014 at 6:15 am

    Left a message out on GitHub… In regards to CSRF integration. Any help would be greatly appreciated. Thanks! – https://github.com/DHTMLX/node-scheduler-demo/issues/1

  2. JB Christy,
    March 4, 2014 at 9:58 pm

    Thanks for this great tutorial and for the sample code! I’m new to node.js and the node ecosystem and really appreciate tutorials like this.

    You mentioned that it wouldn’t be hard to allow several users to update the calendar simultaneously, but “it is a topic for a separate article.” I’d love to learn how to do that. Do you have plans to write that article?

    Thanks again!

    • Ivan,
      March 5, 2014 at 12:25 pm

      Glad that you liked the tutorial. We have plans to write another one that will describe the usecase when several users update the calendar simultaneously but it’s hard to say when it will be available.

  3. Gary Schellhas,
    March 10, 2014 at 11:02 pm

    Could you explain how to add a custom field? I was able to have a checkbox show up and store in the database, but it is not loading correctly when it pulls from the database. Please help!

Leave a Reply