Features

  • Uses DayPilot Angular 2 Scheduler component for scheduling events for multiple resources
  • Use DayPilot.Angular.Modal component to create customized modal dialog.
  • "Create Event" modal dialog is used to enter details of a new event (after selecting a time range in the Scheduler)
  • "Update Event" modal dialgo is used to modify existing Scheduler events (opened upon event click or using a context menu)
  • The sample project uses Angular CLI 1.0.0-beta.25.5
  • Includes a trial version of DayPilot Pro for JavaScript

This tutorial doesn't cover Scheduler basics (installation, configuration). For an introduction to DayPilot Scheduler and Angular 2, please see the following tutorial:

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.

Live Demo

You can test this project in a live demo:

Angular 2 Modal Dialog

DayPilot includes a special Angular 2 version of the modal dialog. The pure JavaScript DayPilot.Modal dialog can't be used because it requires access to the DOM at the top level which doesn't work well with Angular 2.

It has a different appearance and behavior - the Angular 2 version slides down from the top of the page and always uses full page width. This makes it more compatible with mobile devices with small screen.

Creating New Events in the Scheduler

angular2-scheduler-time-range-selecting.png

Handle onTimeRangeSelected event and use it to open the modal dialog.

"Create Event" Modal Dialog

angular2-scheduler-create-event-modal-dialog-empty.png

scheduler.component.ts

import {Component, ViewChild, AfterViewInit, ChangeDetectorRef} from "@angular/core";
import {DayPilot} from "daypilot-pro-angular";
import {DataService} from "./data.service";
import {CreateComponent} from "./create.component";
import {EditComponent} from "./edit.component";

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

  @ViewChild("scheduler") scheduler: DayPilot.Angular.Scheduler;
  @ViewChild("create") create: CreateComponent;
  
  // ...
  
  config: any = {
    // ...
    onTimeRangeSelected: args => {
      this.create.show(args);
    }
    // ...
  };

  // ...

  createClosed(args) {
    if (args.result) {
      this.events.push(args.result);
      this.scheduler.control.message("Created.");
    }
    this.scheduler.control.clearSelection();
  }
  
  // ...

}

create.component.ts

import {Component, ViewChild, Output, EventEmitter} from '@angular/core';
import {DayPilot} from "daypilot-pro-angular";
import Modal = DayPilot.Angular.Modal;
import {Validators, FormBuilder, FormGroup, FormControl} from "@angular/forms";
import {DataService, CreateEventParams} from "./data.service";

@Component({
  selector: 'create-dialog',
  template: `
    <daypilot-modal #modal (close)="closed($event)">
    <div class="center">
      <h1>Create Event</h1>
      <form [formGroup]="form">
        <div class="form-item">
          <input formControlName="name" type="text" placeholder="Event Name"> <span *ngIf="!form.controls.name.valid">Event name required</span>
        </div>
        <div class="form-item">
          <select formControlName="resource">
            <option *ngFor="let r of resources" [ngValue]="r.id">{{r.name}}</option>
          </select>
        </div>
        <div class="form-item">
          <input formControlName="start" type="text" placeholder="Start"> <span *ngIf="!form.controls.start.valid">Invalid datetime</span>
        </div>
        <div class="form-item">
          <input formControlName="end" type="text" placeholder="End"> <span *ngIf="!form.controls.end.valid">Invalid datetime</span>
        </div>
        <div class="form-item">
          <button (click)="submit()" [disabled]="!form.valid">Create</button>
          <button (click)="cancel()">Cancel</button>
        </div>
    </form>
    </div>
    </daypilot-modal>
  `,
  styles: [`
  .center {
    max-width: 800px;
    margin-left: auto;
    margin-right: auto;
  }
  .form-item {
    margin: 4px 0px;
  }
  `]
})
export class CreateComponent {
  @ViewChild("modal") modal : Modal;
  @Output() close = new EventEmitter();

  form: FormGroup;
  dateFormat = "MM/dd/yyyy h:mm tt";

  resources: any[];

  constructor(private fb: FormBuilder, private ds: DataService) {
    this.form = this.fb.group({
      name: ["", Validators.required],
      start: ["", this.dateTimeValidator(this.dateFormat)],
      end: ["", [Validators.required, this.dateTimeValidator(this.dateFormat)]],
      resource: ["", Validators.required]
    });

    this.ds.getResources().subscribe(result => this.resources = result);
  }

  show(args: any) {
    args.name = "";
    this.form.setValue({
      start: args.start.toString(this.dateFormat),
      end: args.end.toString(this.dateFormat),
      name: "",
      resource: args.resource
    });
    this.modal.show();
  }

  submit() {
    let data = this.form.getRawValue();

    let params: CreateEventParams = {
      start: DayPilot.Date.parse(data.start, this.dateFormat).toString(),
      end: DayPilot.Date.parse(data.end, this.dateFormat).toString(),
      text: data.name,
      resource: data.resource
    };

    this.ds.createEvent(params).subscribe(result => {
      //params.id = result.id;
      this.modal.hide(result);
    });
  }

  cancel() {
    this.modal.hide();
  }

  closed(args) {
    this.close.emit(args);
  }

  dateTimeValidator(format: string) {
    return function(c:FormControl) {
      let valid = !!DayPilot.Date.parse(c.value, format);
      return valid ? null : {badDateTimeFormat: true};
    };
  }
}

When the modal dialog is closed the events is updated and control is passed back to the SchedulerComponent.

It displays a message:

angular2-scheduler-event-created.png

"Update Event" Modal Dialog

scheduler.component.ts

import {Component, ViewChild, AfterViewInit, ChangeDetectorRef} from "@angular/core";
import {DayPilot} from "daypilot-pro-angular";
import {DataService} from "./data.service";
import {CreateComponent} from "./create.component";
import {EditComponent} from "./edit.component";

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

  @ViewChild("scheduler") scheduler: DayPilot.Angular.Scheduler;
  //...
  @ViewChild("edit") edit: EditComponent;

  config: any = {
    // ...
    onEventClicked: args => {
      this.edit.show(args.e);
    },
    // ...
  };

  // ...

  editClosed(args) {
    if (args.result) {
      this.scheduler.control.message("Updated");
    }
  }
  
  // ...

}

edit.component.ts

import {Component, ViewChild, Output, EventEmitter} from '@angular/core';
import {DayPilot} from "daypilot-pro-angular";
import Modal = DayPilot.Angular.Modal;
import {Validators, FormBuilder, FormGroup, FormControl} from "@angular/forms";
import {DataService, CreateEventParams, EventData, UpdateEventParams} from "./data.service";

@Component({
  selector: 'edit-dialog',
  template: `
    <daypilot-modal #modal (close)="closed($event)">
    <div class="center">
      <h1>Update Event</h1>
      <form [formGroup]="form">
        <div class="form-item">
          <input formControlName="name" type="text" placeholder="Event Name"> <span *ngIf="!form.controls.name.valid">Event name required</span>
        </div>
        <div class="form-item">
          <select formControlName="resource">
            <option *ngFor="let r of resources" [ngValue]="r.id">{{r.name}}</option>
          </select>
        </div>
        <div class="form-item">
          <input formControlName="start" type="text" placeholder="Start"> <span *ngIf="!form.controls.start.valid">Invalid datetime</span>
        </div>
        <div class="form-item">
          <input formControlName="end" type="text" placeholder="End"> <span *ngIf="!form.controls.end.valid">Invalid datetime</span>
        </div>
        <div class="form-item">
          <button (click)="submit()" [disabled]="!form.valid">Create</button>
          <button (click)="cancel()">Cancel</button>
        </div>
    </form>
    </div>
    </daypilot-modal>
  `,
  styles: [`
  .center {
    max-width: 800px;
    margin-left: auto;
    margin-right: auto;
  }
  .form-item {
    margin: 4px 0px;
  }
  `]
})
export class EditComponent {
  @ViewChild("modal") modal : Modal;
  @Output() close = new EventEmitter();

  form: FormGroup;
  dateFormat = "MM/dd/yyyy h:mm tt";

  resources: any[];

  event: DayPilot.Event;

  constructor(private fb: FormBuilder, private ds: DataService) {
    this.form = this.fb.group({
      name: ["", Validators.required],
      start: ["", this.dateTimeValidator(this.dateFormat)],
      end: ["", [Validators.required, this.dateTimeValidator(this.dateFormat)]],
      resource: ["", Validators.required]
    });

    this.ds.getResources().subscribe(result => this.resources = result);
  }

  show(ev: DayPilot.Event) {
    this.event = ev;
    this.form.setValue({
      start: ev.start().toString(this.dateFormat),
      end: ev.end().toString(this.dateFormat),
      name: ev.text(),
      resource: ev.resource()
    });
    this.modal.show();
  }

  submit() {
    let data = this.form.getRawValue();

    // modify the original object from [events] which is stored in event.data
    this.event.data.start = DayPilot.Date.parse(data.start, this.dateFormat);
    this.event.data.end = DayPilot.Date.parse(data.end, this.dateFormat);
    this.event.data.resource = data.resource;
    this.event.data.text = data.name;

    this.ds.updateEvent(this.event).subscribe(result => {
      this.modal.hide(result);
    });
  }

  cancel() {
    this.modal.hide();
  }

  closed(args) {
    this.close.emit(args);
  }

  dateTimeValidator(format: string) {
    return function(c:FormControl) {
      let valid = !!DayPilot.Date.parse(c.value, format);
      return valid ? null : {badDateTimeFormat: true};
    };
  }
}

Context Menu

angular2-scheduler-context-menu-edit-event.png

The same approach can be used to open the "Update Event" modal dialog using an event context menu.

scheduler.component.ts

import {Component, ViewChild, AfterViewInit, ChangeDetectorRef} from "@angular/core";
import {DayPilot} from "daypilot-pro-angular";
import {DataService} from "./data.service";
import {CreateComponent} from "./create.component";
import {EditComponent} from "./edit.component";

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

  @ViewChild("scheduler") scheduler: DayPilot.Angular.Scheduler;
  // ...
  @ViewChild("edit") edit: EditComponent;

  events: any[] = [];

  config: any = {
    // ...
    contextMenu: new DayPilot.Menu({
      items: [
        { text: "Edit", onClick: args => this.edit.show(args.source) },
        ]
    }),
    // ...
  };

  editClosed(args) {
    if (args.result) {
      this.scheduler.control.message("Updated");
    }
  }

}