Features

  • This is an Angular 12 project that shows how to use Angular Scheduler component from DayPilot Pro for JavaScript.

  • See the list of Scheduler component features. You can test the features in the online demos.

  • In the tutorial, you will find the steps required to create the Angular project from scratch. You will learn how to install and configure the component and load the data (resources and events) using a HTTP service (REST interface).

  • You can also skip the configuration steps and download the attached project and see the Scheduler in action.

  • The project uses PHP to provide the REST API. You can use the Angular Scheduler with any other language and/or framework (see also a tutorial that uses Spring Boot/Java to implement the REST API).

  • The Angular version of DayPilot Pro is available as a special NPM module published at npm.daypilot.org.

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.

Scheduler Configurator

angular 6 scheduler configurator

Instead of creating a new Angular project and configuring the component manually, you can also use the online Scheduler UI Builder. This configurator lets you customize the Scheduler properties and test the behavior using a live preview.

It can also generate a downloadable Angular project for you (including the selected configuration and all dependencies).

1. How to Create an Angular Application with the Scheduler Component

Node.js

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

Install Angular CLI

Install Angular CLI as a global package:

npm install -g @angular/cli

Create a New Angular Project

Create a new Angular project using Angular CLI:

ng new angular-scheduler-php-frontend

Add DayPilot NPM Module

Install daypilot-pro-angular module using the link from npm.daypilot.org:

npm install https://npm.daypilot.org/daypilot-pro-angular/trial/2021.3.5055.tar.gz --save

The updated package.json file will look like this:

{
  "name": "angular-scheduler-php-frontend",
  "license": "SEE LICENSE IN license/LicenseAgreementTrial.pdf",
  "version": "0.0.0",
  "scripts": {
    "ng": "ng",
    "start": "ng serve --proxy-config proxy.conf.json",
    "build": "ng build",
    "watch": "ng build --watch --configuration development",
    "test": "ng test"
  },
  "private": true,
  "dependencies": {
    "@angular/animations": "~12.1.1",
    "@angular/common": "~12.1.1",
    "@angular/compiler": "~12.1.1",
    "@angular/core": "~12.1.1",
    "@angular/forms": "~12.1.1",
    "@angular/platform-browser": "~12.1.1",
    "@angular/platform-browser-dynamic": "~12.1.1",
    "@angular/router": "~12.1.1",
    "daypilot-pro-angular": "https://npm.daypilot.org/daypilot-pro-angular/trial/2021.3.5055.tar.gz",
    "rxjs": "~6.6.0",
    "tslib": "^2.2.0",
    "zone.js": "~0.11.4"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "~12.1.1",
    "@angular/cli": "~12.1.1",
    "@angular/compiler-cli": "~12.1.1",
    "@types/jasmine": "~3.6.0",
    "@types/node": "^12.11.1",
    "jasmine-core": "~3.7.0",
    "karma": "~6.3.0",
    "karma-chrome-launcher": "~3.1.0",
    "karma-coverage": "~2.0.3",
    "karma-jasmine": "~4.0.0",
    "karma-jasmine-html-reporter": "^1.5.0",
    "typescript": "~4.3.2"
  }
}

Note that the DayPilot Angular module (daypilot-pro-angular) is not loaded from npmjs.org registry but it is served from npm.daypilot.org.

Create a Scheduler Module

Create a new directory called scheduler in src/app directory.

In this new directory, create a scheduler.module.ts file with SchedulerModule class:

import {DataService} from './data.service';
import {FormsModule} from '@angular/forms';
import {BrowserModule} from '@angular/platform-browser';
import {NgModule} from '@angular/core';
import {SchedulerComponent} from './scheduler.component';
import {DayPilotModule} from 'daypilot-pro-angular';
import {HttpClientModule} from '@angular/common/http';

@NgModule({
  imports:      [
    BrowserModule,
    FormsModule,
    HttpClientModule,
    DayPilotModule
  ],
  declarations: [
    SchedulerComponent
  ],
  exports:      [ SchedulerComponent ],
  providers:    [ DataService ]
})
export class SchedulerModule { }

The SchedulerModule will declare two classes necessary for our scheduler application:

  • scheduler.component.ts (component class with Scheduler configuration)

  • data.service.ts (service class which provides HTTP services)

Add the Scheduler Component

angular scheduler component typescript php mysql main

The scheduler.component.ts file defines the main component that will display the Scheduler.

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

@Component({
  selector: 'scheduler-component',
  template: `<daypilot-scheduler [config]="config" [events]="events" #scheduler></daypilot-scheduler>`,
  styles: [``]
})
export class SchedulerComponent implements AfterViewInit {

  @ViewChild('scheduler')
  scheduler!: DayPilotSchedulerComponent;

  events: any[] = [];

  config: DayPilot.SchedulerConfig = {
    scale: "Day",
    startDate: DayPilot.Date.today().firstDayOfMonth(),
    days: DayPilot.Date.today().daysInMonth(),
    timeHeaders: [
      {groupBy: "Month"},
      {groupBy: "Day", format: "d"}
    ],
    cellWidthSpec: "Auto",
    resources: [],
    eventHeight: 30,
    treeEnabled: true,
    onBeforeEventRender: args => {
      if (args.data.text && args.data.text === "Vacation") {
        args.data.barColor = "#0F9D58";
        args.data.barBackColor = "#0F9D58";
      }
    },
    onEventMove: args => {
      let data = {
        id: args.e.id(),
        newStart: args.newStart.toString(),
        newEnd: args.newEnd.toString(),
        newResource: args.newResource
      };

      this.ds.moveEvent(data).subscribe(result => {
        this.scheduler.control.message("Updated");
      });
    },
    onEventResize: args => {
      let data = {
        id: args.e.id(),
        newStart: args.newStart.toString(),
        newEnd: args.newEnd.toString(),
        newResource: args.e.resource()  // existing resource id
      };

      this.ds.moveEvent(data).subscribe(result => {
        this.scheduler.control.message("Updated");
      });
    },
    onTimeRangeSelect: args => {

      DayPilot.Modal.prompt("Event name: ", "New event").then(modal => {
        if (modal.canceled) {
          return;
        }
        this.scheduler.control.clearSelection();
        const e: any = {
          id: null,
          start: args.start.toString(),
          end: args.end.toString(),
          text: modal.result,
          resource: args.resource
        };

        this.ds.createEvent(e).subscribe(result => {
          e.id = result.id;
          this.events.push(e);
          this.scheduler.control.message("Created");
        });
      });
    }
  };

  constructor(private ds: DataService) {
  }

  ngAfterViewInit(): void {
    this.ds.getResources().subscribe(result => this.config.resources = result);

    const from = this.scheduler.control.visibleStart();
    const to = this.scheduler.control.visibleEnd();
    this.ds.getEvents(from, to).subscribe(result => {
      this.events = result;
    });
  }

}

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

  • the events attribute specifies the array that holds the Scheduler events

  • the configattribute specifies the object with Scheduler properties

  • we also use #scheduler attribute to get the component reference

Overview of the Angular Application

All Scheduler-related code is now in the src/app/scheduler directory. Now we need to modify the main Angular application classes to display the Scheduler:

Add <scheduler-component> tag to app.component.html file:

<scheduler-component></scheduler-component>

Import SchedulerModule in app.module.ts file:

import {BrowserModule} from '@angular/platform-browser';
import {NgModule} from '@angular/core';
import {AppComponent} from './app.component';
import {SchedulerModule} from "./scheduler/scheduler.module";

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    SchedulerModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Application Source Code Structure

This is the final structure of the Angular project (src/app directory):

angular-scheduler-php-frontend/src/app
+ scheduler
  - data.service.ts
  - scheduler.component.ts
  - scheduler.module.ts
- app.component.css
- app.component.html
- app.component.spec.ts
- app.component.ts
- app.module.ts

How to Run the Angular Scheduler Project

You can download the sample project as a .zip file (see the download link at the top of the article). The download includes two separate projects:

  • angular-scheduler-php-frontend: Angular application that displays the Scheduler

  • angular-scheduler-php-backend: PHP application that provides a REST API to access a MySQL database

How to Run the PHP Backend Project

Run the PHP project using the built-in web server at port 8090:

Linux

php -S 127.0.0.1:8090 -t /home/daypilot/tutorials/angular-scheduler-php-backend

Windows

php.exe -S 127.0.0.1:8090 -t C:\Users\daypilot\tutorials\angular-scheduler-php-backend

How to Run the Angular Frontend Project

If you want to run the downloaded project, you need to install the dependencies (node_modules) first:

npm install

To run the Angular project, execute this command:

ng serve --proxy-config proxy.conf.json

The Angular project uses a proxy to forward requests to /api/* URL to the backend PHP project that is running at port 8090.

proxy-conf.json

{
  "/api": {
    "target": "http://localhost:8090",
    "secure": false
  }
}

2. How to Configure the Angular Scheduler Component

angular scheduler component typescript php mysql configuration

You can configure the Scheduler component by modifying the config object of SchedulerComponent class. Here is an example of basic Scheduler configuration:

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

@Component({
  selector: 'scheduler-component',
  template: `<daypilot-scheduler [config]="config"></daypilot-scheduler>`
})
export class SchedulerComponent {

  config: DayPilot.SchedulerConfig = {};

}

You can use the config attribute to specify the Scheduler properties and event handlers. The properties will let you customize the Scheduler appearance and behavior.

You can use startDate and days properties to define the date range:

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

@Component({
  selector: 'scheduler-component',
  template: `<daypilot-scheduler [config]="config"></daypilot-scheduler>`
})
export class SchedulerComponent {

  config: DayPilot.SchedulerConfig = {
    scale: "Day",
    startDate: DayPilot.Date.today().firstDayOfMonth(),
    days: DayPilot.Date.today().daysInMonth(),
    timeHeaders: [
      {groupBy: "Month"},
      {groupBy: "Day", format: "d"}
    ]
  };

}

Use the scale property to set the duration of grid cells:

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

@Component({
  selector: 'scheduler-component',
  template: `<daypilot-scheduler [config]="config"></daypilot-scheduler>`
})
export class SchedulerComponent {

  config: DayPilot.SchedulerConfig = {
    startDate: DayPilot.Date.today().firstDayOfMonth(),
    days: DayPilot.Date.today().daysInMonth(),
    scale: "Day"
  };

}

Customize the time headers using timeHeaders property:

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

@Component({
  selector: 'scheduler-component',
  template: `<daypilot-scheduler [config]="config"></daypilot-scheduler>`
})
export class SchedulerComponent {

  config: DayPilot.SchedulerConfig = {
    startDate: DayPilot.Date.today().firstDayOfMonth(),
    days: DayPilot.Date.today().daysInMonth(),
    scale: "Day",
    timeHeaders: [
      {groupBy: "Month"},
      {groupBy: "Day", format: "d"}
    ]
  };

}

3. How to Load Row Data

angular scheduler component typescript php mysql resources

The rows are defined using resources property of the Scheduler config object:

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

@Component({
  selector: 'scheduler-component',
  template: `<daypilot-scheduler [config]="config"></daypilot-scheduler>`
})
export class SchedulerComponent {

  @ViewChild('scheduler')
  scheduler!: DayPilotSchedulerComponent;

  config: DayPilot.SchedulerConfig = {
    scale: "Day",
    startDate: DayPilot.Date.today().firstDayOfMonth(),
    days: DayPilot.Date.today().daysInMonth(),
    timeHeaders: [
      {groupBy: "Month"},
      {groupBy: "Day", format: "d"}
    ],
    resources: [
      {id:"group_1", name: "People", expanded: true, children: [
        {id: 1, name: "Person 1"},
        {id: 2, name: "Person 2"},
        {id: 3, name: "Person 3"}
      ]},
      {id: "group_2", name: "Tools", expanded: true, children: [
        {id: 4, name: "Tool 1"},
        {id: 5, name: "Tool 2"},
        {id: 6, name: "Tool 3"}
      ]}
    ]
  };

}

Because we want to load the row data from the database we need to replace the static definition with an HTTP call:

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

@Component({
  selector: 'scheduler-component',
  template: `<daypilot-scheduler [config]="config"></daypilot-scheduler>`
})
export class SchedulerComponent implements AfterViewInit {

  @ViewChild('scheduler')
  scheduler!: DayPilotSchedulerComponent;

  config: DayPilot.SchedulerConfig = {
    scale: "Day",
    startDate: DayPilot.Date.today().firstDayOfMonth(),
    days: DayPilot.Date.today().daysInMonth(),
    timeHeaders: [
      {groupBy: "Month"},
      {groupBy: "Day", format: "d"}
    ],
    resources: []
  };

  constructor(private ds: DataService) {
  }
  
  ngAfterViewInit(): void {
    this.ds.getResources().subscribe(result => this.config.resources = result);
  }  

}

The DataService class makes the HTTP call and returns an array with resource data:

import {Injectable} from '@angular/core';
import {DayPilot} from 'daypilot-pro-angular';
import {HttpClient} from '@angular/common/http';
import {Observable} from 'rxjs';

@Injectable()
export class DataService {

  constructor(private http: HttpClient) {
  }

  getResources(): Observable<any[]> {
    return this.http.get("/api/backend_resources.php") as Observable<any>;
  }

}

The PHP implementation of the backend_resources.php endpoint looks like this:

<?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 JSON response corresponds to the structure required by DayPilot.Scheduler.resources array.

Example 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":"group_2","name":"Tools","expanded":true,"children":[
    {"id":"4","name":"Tool 1"},
    {"id":"5","name":"Tool 2"},
    {"id":"6","name":"Tool 3"}
  ]}
]

4. How to Load Event Data

angular scheduler component typescript php mysql events

The next step will be loading the Scheduler events. We will use the same approach that we used for loading resources:

  • Subscribe to DataService.getEvents() in ngAfterViewInit() and save them to events object

  • DataService.getEvents() makes an HTTP call to the backend PHP project to get the event data

Note that the events are stored in a special events object, which is referenced using [events] attribute of <daypilot-scheduler>.

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

@Component({
  selector: 'scheduler-component',
  template: `<daypilot-scheduler [config]="config" [events]="events"></daypilot-scheduler>`
})
export class SchedulerComponent implements AfterViewInit {

  events: any[] = [];

  config: DayPilot.SchedulerConfig = {
    scale: "Day",
    startDate: DayPilot.Date.today().firstDayOfMonth(),
    days: DayPilot.Date.today().daysInMonth(),
    timeHeaders: [
      {groupBy: "Month"},
      {groupBy: "Day", format: "d"}
    ],
    resources: []
  };

  constructor(private ds: DataService) {
  }
  
  ngAfterViewInit(): void {
    this.ds.getResources().subscribe(result => this.config.resources = result);

    const from = this.scheduler.control.visibleStart();
    const to = this.scheduler.control.visibleEnd();
    this.ds.getEvents(from, to).subscribe(result => {
      this.events = result;
    });
  }  

}

data.service.ts

import {Injectable} from '@angular/core';
import {DayPilot} from 'daypilot-pro-angular';
import {HttpClient} from '@angular/common/http';
import {Observable} from 'rxjs';

@Injectable()
export class DataService {

  constructor(private http: HttpClient) {
  }

  getEvents(from: DayPilot.Date, to: DayPilot.Date): Observable<any[]> {
    return this.http.get("/api/backend_events.php?from=" + from + "&to=" + to) as Observable<any>;
  }

  // ...

}

The REST endpoint (backend_events.php) is defined in the PHP project. It queries the database and returns the event data for the selected date range.

backend_events.php

<?php
require_once '_db.php';

$stmt = $db->prepare('SELECT * FROM events WHERE NOT ((end <= :start) OR (start >= :end))');
$stmt->bindParam(':start', $_GET["from"]);
$stmt->bindParam(':end', $_GET["to"]);
$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":"1","text":"Activity","start":"2021-06-04T00:00:00","end":"2021-06-07T00:00:00","resource":"1"}]

Change History

  • August 22, 2021: Upgraded to Angular 12, DayPilot Pro for JavaScript 2021.3.5055

  • December 20, 2020: Upgraded to Angular 11, DayPilot Pro for JavaScript 2020.4.2807

  • June 3, 2020: Upgraded to Angular 9, DayPilot Pro for JavaScript 2020.2.4481

  • September 4, 2019: Upgraded to Angular 8, DayPilot Pro for JavaScript 2019.2.3871

  • June 5, 2018: Upgraded to Angular 6, DayPilot Pro for JavaScript 2018.2.3297, Angular CLI 6.0

  • September 19, 2016: PHP backend added.

  • 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 15, 2016: Build 8.2.2381 includes TypeScript definitions of all event handlers. A generic EventHandler interface is used.

  • September 15, 2016: Initial release, based on DayPilot Pro for JavaScript 8.2.2380, Angular 2.0.0