License

Licensed for testing and evaluation purposes. Please see the license agreement included in the sample project. You can use the source code of the tutorial if you are a licensed user of DayPilot Pro for JavaScript. Buy a license.

Requirements

  • PHP 5

AJAX Scheduler Features

This web application allows drag and drop event scheduling using DayPilot JavaScript scheduler component.

  • HTML5 scheduler widget

  • Loads events from a sample SQLite database

  • Uses PHP on the server side

  • Handles drag and drop event moving and resizing

  • Displays detailed information about the event on hover

JavaScript Scheduler Setup

In order to start using the AJAX Scheduler in your application, you need to include daypilot-all.min.js library. The default CSS theme is included in the library so this is the only file needed to get started. We will see how to apply a custom CSS theme later.

<script src="js/daypilot/daypilot-all.min.js" type="text/javascript"></script>

The next step is to add a Scheduler placeholder to the HTML. This is the location where the Scheduler will be displayed.

<div id="dp"></div>

And the last step is to add the Scheduler JavaScript initialization code. This section will include the configuration options and event handlers. The Scheduler initialization can be moved to a special JavaScript file (e.g. scheduler-init.js) and included using <script src="scheduler-init.js"> tag. Just make sure that the initialization JavaScript is loaded after the main library (daypilot-all.min.js), otherwise the browser will complain that DayPilot object is undefine.

<script type="text/javascript">
    var dp = new DayPilot.Scheduler("dp");
    dp.init();
</script>

Configure the Scheduler Appearance

ajax-scheduler-javascript-php-time-header.png

Now you have a working Scheduler instance which is actually visible on your page but it uses the default configuration and doesn’t display any data.

First, we configure the Scheduler time axis using the following properties:

  1. Configure the cell dimensions using eventHeight and cellWidth properties.

  2. Set the time scale using scale property.

  3. Set the date range using startDate and days properties.

<script type="text/javascript">
    var dp = new DayPilot.Scheduler("dp");

    // behavior and appearance
    dp.cellWidth = 40;
    dp.eventHeight = 30;
    dp.headerHeight = 25;

    // view
    dp.startDate = new DayPilot.Date("2020-08-01");
    dp.days = dp.startDate.daysInMonth();
    dp.scale = "Day";
    dp.timeHeaders = [
      { groupBy: "Month" },
      { groupBy: "Day", format: "d" }
    ];

    dp.init();

</script>

Our Scheduler widget is still empty (no rows, no grid) but it displays the correct time header.

Apply a CSS Theme

ajax-scheduler-javascript-php-css-theme.png

The Scheduler appearance can be easily customized using CSS themes. As we mentioned at the beginning, the default CSS theme is built in and doesn’t use any special configuration. However, you may want to modify the appearance and use the color scheme that matches your application.

You can create the CSS theme manually by specifying the styles for individual CSS classes, but there is an online application that will let you define the basic properties using in a WYSIWYG mode: DayPilot theme designer. You can use it to specify the colors, alignment, padding, fonts and other properties of common Scheduler elements. It generates a CSS file that you can download and include in your application. The CSS theme designer doesn’t cover all options and if the output still doesn’t meet your needs, you can edit the CSS file manually.

As soon as the theme is ready (we will use “Theme 8” which is included in the download package) and included in your page (<link> tag) you can apply it using thetheme property.

<link type="text/css" rel="stylesheet" href="themes/scheduler_8.css" />    

<script type="text/javascript">
    var dp = new DayPilot.Scheduler("dp");

    // ...
    dp.theme = "scheduler_8";
    // ...

    dp.init();

</script>

Configure Scheduler Resources

ajax-scheduler-javascript-php-row-header.png

We want the AJAX Scheduler to display a hierarchy of resources (rooms) on the right. The resource tree is supported but we need to enable it explicitly using treeEnabled property.

Now we can define the rows using resources. This is an array of objects which define resources using name and id properties. The name property will be used in the UI and the Scheduler will display it in the name header. The id will be used to identify the resource. This is important when loading the event data - the resource property of the event object has to match the resource id. It determines the row where the event will be displayed.

In this example, we define the rows statically on the client side. This works for very simple applications but usually you will load it from the server side using rows.load() - see also Resource loading in the documentation.

<script type="text/javascript">
    var dp = new DayPilot.Scheduler("dp");
    // ...
    dp.treeEnabled = true;
    dp.resources = [
        {name: "Main Building", id: "G1", expanded: true, children: [
                {name: "Room A", id: "A"},
                {name: "Room B", id: "B"},
                {name: "Room C", id: "C"},
                {name: "Room D", id: "D"},
            ]}
    ];
    // ...
    dp.init();
</script>

Load Events using an AJAX Call

ajax-scheduler-javascript-php-events.png

You can load the scheduler events during initialization using events.list property.

<script type="text/javascript">
    var dp = new DayPilot.Scheduler("dp");
    // ...
    dp.events.list = [
    {
      start: "2020-08-25T00:00:00",
      end: "2020-08-27T00:00:00",
      id: "1",
      resource: "A",
      text: "Event 1"
    },
    {
      start: "2020-08-26T12:00:00",
      end: "2020-08-27T00:00:00",
      id: "2",
      resource: "B",
      text: "Event 2"
    }
    ];
    // ...
    dp.init();
</script>

However, this is not flexible enough as generating inline JSON structures using PHP is not convenient. We will use a special PHP script to generate the event data in JSON format and ask the Scheduler to load it using an AJAX call. You can use standard jQuery $.get() or $.ajax() method but it is not necessary - the Scheduler has a convenience events.load() method that makes the AJAX call for you and displays the result automatically. Just make sure that the data format returned by the JSON script matches the event data object structure.

<script type="text/javascript">
    var dp = new DayPilot.Scheduler("dp");
    // ...
    dp.init();

    loadEvents();

    function loadEvents() {
        dp.events.load("ajax_backend_events.php");
    }

</script>

And this is the PHP script that returns the event data in JSON format (ajax_backend_events.php). You can see that it defines just a few basic properties (id, name, start, end, resource) that are required by the Scheduler.

<?php
require_once '_db.php';
    
$result = $db->query('SELECT * FROM events');

class Event {}
$events = array();

foreach($result as $row) {
  $e = new Event();
  $e->id = $row['id'];
  $e->text = $row['name'];
  $e->start = $row['start'];
  $e->end = $row['end'];
  $e->resource = $row['resource'];
  $events[] = $e;
}

echo json_encode($events);

Drag and Drop Event Moving

ajax-scheduler-javascript-php-drag-and-drop-moving.png

The AJAX Scheduler has many features that will let your users control the scheduled events. The most common ones are enabled by default, and that includes drag and drop event moving. Without any additional tweaks, the users can drag the event to a different row and start time.

However, it is necessary to make the changes persistent by saving them in the database on the server side. The Scheduler component provides an API for that - there is the onEventMoved event handler that will notify you when an event has been moved to another location.

You can use it to make an AJAX call to the server and store the change. We will use the built-in DayPilot.Http.ajax() method to make the AJAX call. It provides an API similar to jQuery. If you already use jQuery in your application you can use it but if you don’t you can use the built-in helper method to avoid additional dependency.

The DayPilot.Http.ajax() method uses JSON format for sending the data to the server side. We will use the args parameter of the onEventMoved event handler as the data parameter. This can be done because the args object defines toJSON() method that makes sure that the parameters are serialized properly to a simple object. The serialized args object includes id, newStart, newEnd, and newResource properties. That is all we need to make the database change.

<script type="text/javascript">
  var dp = new DayPilot.Scheduler("dp");

  // ...

  // http://api.daypilot.org/daypilot-scheduler-oneventmoved/ 
  dp.onEventMoved = function (args) {
    DayPilot.Http.ajax({
      url: "ajax_backend_move.php",
      data: args,
      success: function (ajax) { // success
        var response = ajax.data;
        if (response && response.result) {
          dp.message("Moved: " + response.message);
        }
      },
      error: function(ajax) {
        dp.message("Saving failed");
      }
    });
  };
  
  // ...
  
  dp.init();
</script>

And this is the server-side PHP script that will be called when an event is moved (ajax_backend_move.php).

<?php
require_once '_db.php';

$insert = "UPDATE events SET start = :start, end = :end, resource = :resource WHERE id = :id";

$stmt = $db->prepare($insert);

$stmt->bindParam(':start', $start);
$stmt->bindParam(':end', $end);
$stmt->bindParam(':id', $id);
$stmt->bindParam(':resource', $resource);

$received = json_decode(file_get_contents('php://input'));

$id = $received->e->id;
$start = $received->newStart;
$end = $received->newEnd;
$resource = $received->newResource;
$stmt->execute();

class Result {}

$response = new Result();
$response->result = 'OK';
$response->message = 'Update successful';

echo json_encode($response);

Drag and Drop Event Creating

ajax-scheduler-javascript-php-drag-and-drop-event-creating.png

The Scheduler has an API for creating new events as well - it lets you select a time range using drag and drop and handle it using onTimeRangeSelected event handler. There is no default action associated with the onTimeRangeSelected event - the Scheduler does know the location (row, start and end date) but it has no idea about other event properties (such as the text). It’s up to you to provide it (or ask the user) and create the event explicitly.

The following onTimeRangeSelected event handler asks for the new event name using DayPilot.Modal.prompt() method (a convenience method that replaces the built-in JavaScript prompt() method). If the users provides the event name it makes an AJAX call to ajax_backend_create.php PHP script which stores the new event in the database and returns the auto-generated record id. Only then (when the AJAX call is successful) is the event actually added to the Scheduler using events.add() method.

It also displays the status message (returned from the server) using message() method.

<script type="text/javascript">
  var dp = new DayPilot.Scheduler("dp");

  // ...

  // http://api.daypilot.org/daypilot-scheduler-oneventmoved/ 
  dp.onTimeRangeSelected = function (args) {
    var name = DayPilot.Modal.prompt("New event name:", "Event").then(function(modal) {
      dp.clearSelection();

      if (modal.canceled) {
        return;
      }

      var name = modal.result;

      var e = {
        start: args.start,
        end: args.end,
        resource: args.resource,
        text: name
      };

      DayPilot.Http.ajax({
        url: "ajax_backend_create.php",
        data: e,
        success: function(ajax) {
          var response = ajax.data;
          if (response && response.result) {
            e.id = response.id;
            dp.events.add(e);
            dp.message("Created: " + response.message);
          }
        },
        error: function(ajax) {
          dp.message("Saving failed");
        }
      });
    });
  };
  
  // ...
  
  dp.init();
</script>

This is the server-side ajax_backend_create.php script that creates a new database record for the event. Note that the auto-generated record ID is read using $db->lastInsertId() and included in the response.

<?php
require_once '_db.php';

$insert = "INSERT INTO events (name, start, end, resource) VALUES (:name, :start, :end, :resource)";

$stmt = $db->prepare($insert);

$stmt->bindParam(':start', $start);
$stmt->bindParam(':end', $end);
$stmt->bindParam(':name', $name);
$stmt->bindParam(':resource', $resource);

$received = json_decode(file_get_contents('php://input'));

$start = $received->start;
$end = $received->end;
$resource = $received->resource;
$name = $received->text;
$stmt->execute();

class Result {}

$response = new Result();
$response->result = 'OK';
$response->message = 'Created with id: '.$db->lastInsertId();
$response->id = $db->lastInsertId();

echo json_encode($response);

Display Event Details on Hover

ajax-scheduler-javascript-php-event-bubble-details.png

The event box size is defined by start and end time and it may not always include the full text. Users may have troubles distinguishing similar events or reading important details. The Scheduler can help with this - it includes an integrated callout control that can display additional details on hover.

As you can see in the event bubble documentation, the callout content can be defined in the event data object using bubbleHtml property. But it’s also possible to generate the content dynamically (which can be useful if you want to take the current Scheduler state into account).

And that is what we are going to do in our example. In order to generate the bubble content using JavaScript, we need to define a custom bubble using DayPilot.Bubble object and assign it to bubble property.

The Scheduler will call onLoad event handler of the bubble when the callout is to be displayed. That lets you define the HTML using args.html property.

<script type="text/javascript">
  var dp = new DayPilot.Scheduler("dp");

  // ...

  dp.bubble = new DayPilot.Bubble({
    onLoad: function (args) {
      var ev = args.source;
      args.async = true;  // notify manually using .loaded()

      // simulating slow server-side load
      setTimeout(function () {
        args.html = "<div style='font-weight:bold'>" + ev.text() + "</div><div>Start: " + ev.start().toString("MM/dd/yyyy HH:mm") + "</div><div>End: " + ev.end().toString("MM/dd/yyyy HH:mm") + "</div><div>Id: " + ev.id() + "</div>";
        args.loaded();
      }, 500);

    }
  });
  
  // ...
  
  dp.init();
</script>