Features

  • Sample project that shows how to use DayPilot Scheduler with Angular 2.
  • It uses npm package manager to handle dependencies.
  • The Angular 2 version of DayPilot Pro is available as a special npm module published at npm.daypilot.org.
  • Configuration required for using the Scheduler.
  • Loading resources and events using a HTTP service (REST interface).
  • Drag and drop event moving and resizing.

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.

Node.js

You'll need npm package manager to resolve the dependcies (Angular 2, DayPilot). You can install npm as part of node.js.

Angular 2 Scheduler Sample Project

You can download the sample project as a .zip file (see the download link at the top of the article).

To install the dependencies, run:

npm install

To run the sample project, run:

npm start

Below are the steps required to create the project from scratch.

1. Project Directory

Create a new directory called "TutorialAngular2Scheduler".

2. Module Dependencies (package.json)

Create package.json file with the following content:

{
  "name": "TutorialAngular2Scheduler",
  "description": "Angular 2 DayPilot Scheduler Tutorial",
  "private": true,
  "scripts": {
    "start": "live-server"
  },
  "dependencies": {
    "@angular/common": "2.0.0",
    "@angular/compiler": "2.0.0",
    "@angular/core": "2.0.0",
    "@angular/forms": "2.0.0",
    "@angular/http": "2.0.0",
    "@angular/platform-browser": "2.0.0",
    "@angular/platform-browser-dynamic": "2.0.0",
    "@angular/router": "3.0.0",

    "core-js": "^2.4.0",
    "rxjs": "^5.0.0-beta.12",
    "systemjs": "0.19.37",
    "zone.js": "^0.6.21",

    "daypilot-pro-angular": "http://npm.daypilot.org/angular/pro/trial/daypilot-pro-angular-8.2.2385.tar.gz"
  },
  "devDependencies": {
    "live-server": "0.8.2",
    "typescript": "^2.0.0"
  }
}

It includes the dependencies:

  • Angular 2 modules and their dependencies (required)
  • DayPilot Angular 2 module (required)
  • live-script (a web server that you can use to run the project, use "npm start" command)
  • TypeScript module

Note that the DayPilot Angular 2 module is not loaded from npmjs but it is served from npm.daypilot.org.

Load the dependencies by running:

npm install

3. System.js Configuration (systemjs.config.js) 

Create systemjs.config.js file with the following content:

System.config({
    transpiler: 'typescript',
    typescriptOptions: {emitDecoratorMetadata: true},
    map: {
        '@angular': 'node_modules/@angular',
        'rxjs'    : 'node_modules/rxjs',
        'daypilot-pro-angular': 'node_modules/daypilot-pro-angular'
    },
    paths: {
        'node_modules/@angular/*': 'node_modules/@angular/*/bundles'
    },
    meta: {
        '@angular/*': {'format': 'cjs'}
    },
    packages: {
        'app'                              : {main: 'main', defaultExtension: 'ts'},
        'rxjs'                             : {main: 'Rx'},
        '@angular/core'                    : {main: 'core.umd.min.js'},
        '@angular/common'                  : {main: 'common.umd.min.js'},
        '@angular/compiler'                : {main: 'compiler.umd.min.js'},
        '@angular/http'                    : {main: 'http.umd.min.js'},
        '@angular/platform-browser'        : {main: 'platform-browser.umd.min.js'},
        '@angular/platform-browser-dynamic': {main: 'platform-browser-dynamic.umd.min.js'},
        'daypilot-pro-angular'             : {main: 'daypilot-angular.min.js'}
    }
});

4. Main HTML5 File (index.html)

Create the main HTML file (index.html):

<!DOCTYPE html>
<html>
<head>
    <title>Angular 2 DayPilot Scheduler Tutorial</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <script src="node_modules/typescript/lib/typescript.js"></script>
    <script src="node_modules/core-js/client/shim.min.js"></script>
    <script src="node_modules/zone.js/dist/zone.js"></script>
    <script src="node_modules/systemjs/dist/system.src.js"></script>
    <script src="systemjs.config.js"></script>
    <script>
        System.import('app').catch(function(err){ console.error(err); });
    </script>
</head>

<body>
<app>Loading...</app>
</body>
</html>

5. Angular 2 Bootstrap File (main.ts)

Create app/main.ts with the following content:

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule }  from './app.module';
platformBrowserDynamic().bootstrapModule(AppModule);

6. Angular 2 Application Module (app.module.ts)

Create app/app.module.ts with the following content:

import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent }  from './app.component';
import { DayPilot }      from 'daypilot-pro-angular';

@NgModule({
    imports:      [ BrowserModule ],
    declarations: [ AppComponent, DayPilot.Angular.Scheduler ],
    bootstrap:    [ AppComponent ]
})
export class AppModule { }

7. Angular 2 Main Component (app.components.ts)

angular-2-scheduler-typescript-main.png

This is the main component that will display the Scheduler.

The template includes three elements:

  • Header ("Scheduler")
  • DayPilot Scheduler Angular 2 Component (<daypilot-scheduler>)
  • A button that adds a new event to the data source
import {Component, ViewChild} from '@angular/core';
import {DayPilot} from "daypilot-pro-angular";

@Component({
    selector: 'app',
    template: `
        <h1>{{name}}</h1>
        <daypilot-scheduler [events]="events" [config]="config" #scheduler1></daypilot-scheduler>
        <button (click)="add()">Add event</button>
        `
})
export class AppComponent {
    name: string;
    last: number = 1;
    @ViewChild('scheduler1') scheduler1: DayPilot.Angular.Scheduler;

    events: any[] = [
        {start: "2016-09-09", end: "2016-09-10", id: 1, text: "Event 1", resource: "R1"}
    ];

    config: any = {
        scale: "Day",
        startDate: "2016-09-01",
        days: new DayPilot.Date("2016-09-09").daysInMonth(),
        timeHeaders: [
            {groupBy: "Month"},
            {groupBy: "Day", format: "d"}
        ],
        cellWidthSpec: "Auto",
        resources: [
            {name: "R1", id: "R1"},
            {name: "R2", id: "R2"},
            {name: "R3", id: "R3"},
        ],
        onTimeRangeSelected: args => {
            alert("start: " + args.start);
        },
        onEventClicked: args => {
            alert("clicked: " + args.e.text());
        },
        onEventMoved: args => {
            this.scheduler1.control.message("Moved");
        },
        onEventResized: args => {
            this.scheduler1.control.message("Moved");
        }
    };

    constructor() {
        this.name = 'Scheduler';
    }

    add() {
        this.last += 1;
        this.events.push({start: "2016-09-09", end: "2016-09-10", id: this.last, text: "Event " + this.last, resource: "R1"});
        this.scheduler1.control.message("Added");
    }


}

This component uses <daypilot-scheduler> element to load the Scheduler:

  • "events" attribute specifies the array that holds the Scheduler events
  • "config"attribute specifies the object with Scheduler properties
  • we also use #scheduler1 attribute to get the component reference

You can use the config attribute to specify the Scheduler properties and event handlers.

9. Final Directory Structure

This is the final structure of the boilerplate Angular 2 project (TutorialAngular2Scheduler.xxxx.zip):

TutorialAngular2Scheduler
+ app
  - app.module.ts
  - app.component.ts
  - main.ts
- index.html
- package.json
- systemjs.config.js

10. Loading Resources and Events into the Angular 2 Scheduler

angular-2-scheduler-typescript-loading-events.png

Now we want to replace our statically-defined resources and events with data loaded dynamically from the server.

We will load the data in the OnInit handler:

import {Component, ViewChild, OnInit} from '@angular/core';
import {DayPilot} from 'daypilot-pro-angular';
import {DataService} from './data.service';

@Component({
    selector: 'app',
    providers: [ DataService ],
    template: `
        <h1>{{name}}</h1>
        <daypilot-scheduler [events]="events" [config]="config" #scheduler1></daypilot-scheduler>
        `
})
export class AppComponent implements OnInit {
    name: string;
    last: number = 1;
    @ViewChild('scheduler1') scheduler1: DayPilot.Angular.Scheduler;

    events: any[];

    config: any = {
        scale: "Day",
        startDate: "2016-09-01",
        days: new DayPilot.Date("2016-09-01").daysInMonth(),
        timeHeaders: [
            {groupBy: "Month"},
            {groupBy: "Day", format: "d"}
        ],
        cellWidthSpec: "Auto",
        resources: [],
        treeEnabled: true
    };

    constructor(private ds: DataService) {
        this.name = 'Scheduler';
    }

    ngOnInit(){
        this.ds.getEvents().subscribe(data => this.events = data);
        this.ds.getResources().subscribe(data => this.config.resources = data);
    }

}

Note that our component class now implements OnInit:

export class AppComponent implements OnInit {

We have added a new method called ngOnInit():

ngOnInit(){
    this.ds.getEvents().subscribe(data => this.events = data);
    this.ds.getResources().subscribe(data => this.config.resources = data);
}

The "ds" property holds a reference to DataService class which is injected by Angular 2. We need to request it in the constructor:

constructor(private ds: DataService) {
    // ..
}

The service is registered in "providers" array in the component annotation:

@Component({
    // ...
    providers: [ DataService ],
    // ...
})
export class AppComponent implements OnInit {
  // ...
}

The DataService class provides two methods:

  • getEvents() - loads the Scheduler data (events) from "backend_events.php" in JSON format
  • getResources() - loads the Scheduler rows (resources) from "backend_resources.php" in JSON format

data.service.ts

import { Http, Response } from '@angular/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Rx';
import 'rxjs/Rx';
import {DayPilot} from 'daypilot-pro-angular';

@Injectable()
export class DataService {

    constructor(private http : Http){
    }

    getEvents(): Observable<any[]> {
        let body = JSON.stringify({
            start: "2016-09-01T00:00:00",
            end: "2016-09-30T00:00:00",
        });

        return this.http.post("backend_events.php", body).map((response:Response) => response.json());
    }

    getResources(): Observable<any[]> {
        return this.http.get("backend_resources.php").map((response:Response) => response.json());
    }

    // ...

}

export interface DataResponse {
    result: string;
    id?: number;
    message?: string;
}

angular-2-scheduler-typescript-resources.png

On the server side (backend_resources.php), we load the data from a database and return a JSON string.

backend_resources.php

<?php
require_once '_db.php';
    
$scheduler_groups = $db->query('SELECT * FROM [groups] ORDER BY [name]');

class Group {}
class Resource {}

$groups = array();

foreach($scheduler_groups as $group) {
  $g = new Group();
  $g->id = "group_".$group['id'];
  $g->name = $group['name'];
  $g->expanded = true;
  $g->children = array();
  $groups[] = $g;
  
  $stmt = $db->prepare('SELECT * FROM [resources] WHERE [group_id] = :group ORDER BY [name]');
  $stmt->bindParam(':group', $group['id']);
  $stmt->execute();
  $scheduler_resources = $stmt->fetchAll();  
  
  foreach($scheduler_resources as $resource) {
    $r = new Resource();
    $r->id = $resource['id'];
    $r->name = $resource['name'];
    $g->children[] = $r;
  }
}

header('Content-Type: application/json');
echo json_encode($groups);

?>

The structure of the response JSON corresponds to the structure required by DayPilot.Scheduler.resources array.

Sample JSON response:

[
  {"id":"group_1","name":"People","expanded":true,"children":[
    {"id":"1","name":"Person 1"},
    {"id":"2","name":"Person 2"},
    {"id":"3","name":"Person 3"},
    {"id":"4","name":"Person 4"},
    {"id":"5","name":"Person 5"},
    {"id":"6","name":"Person 6"},
    {"id":"7","name":"Person 7"},
    {"id":"8","name":"Person 8"},
    {"id":"9","name":"Person 9"}
  ]},
  {"id":"group_2","name":"Tools","expanded":true,"children":[
    {"id":"11","name":"Tool 1"},
    {"id":"12","name":"Tool 2"},
    {"id":"13","name":"Tool 3"},
    {"id":"14","name":"Tool 4"},
    {"id":"15","name":"Tool 5"},
    {"id":"16","name":"Tool 6"},
    {"id":"17","name":"Tool 7"},
    {"id":"18","name":"Tool 8"},
    {"id":"19","name":"Tool 9"}
  ]}
]

angular-2-scheduler-typescript-events.png

Another endpoints (backend_events.php) returns the event data. It only selects the events from a given date range which is provided in the POST body as a JSON object.

backend_events.php

<?php
require_once '_db.php';

$json = file_get_contents('php://input');
$params = json_decode($json);
    
$stmt = $db->prepare('SELECT * FROM [events] WHERE NOT ((end <= :start) OR (start >= :end))');
$stmt->bindParam(':start', $params->start);
$stmt->bindParam(':end', $params->end);
$stmt->execute();
$result = $stmt->fetchAll();

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_id'];
  $events[] = $e;
}

header('Content-Type: application/json');
echo json_encode($events);

?>

Sample JSON response:

[
  {"id":"2","text":"New event","start":"2016-09-06T00:00:00","end":"2016-09-10T00:00:00","resource":"2"}
]

Change History

  • September 15, 2016: Initial release, based on DayPilot Pro for JavaScript 8.2.2380, Angular 2.0.0
  • September 15, 2016: Build 8.2.2381 includes TypeScript definitions of all event handlers. A generic EventHandler interface is used.
  • September 17, 2016: Build 8.2.2384 includes complete TypeScript definitions for DayPilot.Scheduler and related classes (DayPilot.Date, DayPilot.Event, DayPilot.Bubble, DayPilot.Menu, DayPilot.Locale, DayPilot.Row).
  • September 19, 2016: PHP backend added.