Overview

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.

Live Demo

Scheduler Zoom Levels

The Angular Scheduler component from DayPilot Pro package includes support for zoom. You can define custom zoom levels - each level sets predefined/calculated Scheduler properties. Typically, each level will set scaletimeHeadersstartDate, and days properties.

The zoom levels need to be defined using zoomLevels property of the Scheduler config object:

config: DayPilot.SchedulerConfig = {

    // ...

    zoomLevels: [
      {
        name: "Year",
        properties: {
          scale: "Day",
          cellWidth: 40,
          timeHeaders: [{groupBy: "Year"}, {groupBy: "Month", format: "MMMM"}, {groupBy: "Day", format: "d"}],
          startDate: (args: ZoomLevelCallbackArgs) => { return args.date.firstDayOfYear(); },
          days: (args: ZoomLevelCallbackArgs) => { return args.date.daysInYear(); },
        }
      },
      {
        name: "Month",
        properties: {
          scale: "CellDuration",
          cellDuration: 720,
          cellWidth: 40,
          timeHeaders: [{groupBy: "Month"}, {groupBy: "Day", format: "ddd d"}, {groupBy: "Cell", format: "tt"}],
          startDate: (args: ZoomLevelCallbackArgs) => { return args.date.firstDayOfMonth(); },
          days: (args: ZoomLevelCallbackArgs) => { return args.date.daysInMonth();},
        }
      },
      {
        name: "Week",
        properties: {
          scale: "Hour",
          cellWidth: 40,
          timeHeaders: [{groupBy: "Month"}, {groupBy: "Day", format: "dddd d"}, {groupBy: "Hour"}],
          startDate: (args: ZoomLevelCallbackArgs) => { return args.date.firstDayOfWeek(); },
          days: (_args: ZoomLevelCallbackArgs) => { return 7; },
        }
      },
      {
        name: "Hour",
        properties: {
          scale: "CellDuration",
          cellDuration: 15,
          cellWidth: 40,
          timeHeaders: [{groupBy: "Day", format: "dddd MMMM d, yyyy"}, {groupBy: "Hour"}, {groupBy: "Cell"}],
          startDate: (args: ZoomLevelCallbackArgs) => { return args.date.getDatePart(); },
          days: (_args: ZoomLevelCallbackArgs) => { return 1; },
        }
      },
    ]


};

It is an array of zoom level objects. The zoom level object has the following structure:

{
  customProperty1: "ignored",
  // ...
  properties: {
    property1: value,
    // ...
  }
}

The properties field specifies the Scheduler properties that will be applied when switching to this level. Other properties (e.g. customProperty1) at this level are ignored and you can use them to store custom data (here we use it to store the zoom level name).

The property names correspond to the Scheduler properties. If the property value is a function, the function will be executed and the result will be assigned to the specified Scheduler property. The function will receive a special args object as a parameter.

The args object has the following structure:

  • args.date - the reference date that is calculated from the current Scheduler viewport

  • args.level - the new zoom level object (the parent zoomLevel item)

Switching the Zoom Level

The zoom level can be switched using zoom property of the Scheduler config:

this.config.zoom = 1;

We are using two controls that will let users to change the zoom level.

  • range input control

  • plus/minus buttons

HTML5 Range Control

angular scheduler zoom range

The range control simply uses config.zoom as the [(ngModel)] attribute value:

<input type="range" min="0" max="3" step="1" [(ngModel)]="config.zoom" />

Plus/Minus Buttons

angular scheduler zoom plus minus buttons

The plus and minus buttons increase/decrease the config.zoom value:

HTML

<button id="minus" (click)="minus()">-</button>
<button id="plus" (click)="plus()">+</button>

TypeScript

plus(): void {
  this.config.zoom++;
}

minus(): void {
  this.config.zoom--;
}

However, such simple setup would result in an error if the config.zoom value is too low or high. We need to check the zoomLevels array boundaries:

plus(): void {
  // checking boundaries
  this.config.zoom = Math.min(this.config.zoom as number + 1, this.config.zoomLevels.length - 1);
}

minus(): void {
  // checking boundaries
  this.config.zoom = Math.max(this.config.zoom as number - 1, 0);
}

Full Source Code

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

@Component({
  selector: 'scheduler-component',
  template: `
    <div class="controls">
      <button id="minus" (click)="minus()">-</button>
      <input type="range" min="0" max="3" step="1" [(ngModel)]="config.zoom" />
      <button id="plus" (click)="plus()">+</button>
      <span id="label">{{currentZoomLevelName}}</span>
    </div>

    <daypilot-scheduler [config]="config" [events]="events" #scheduler></daypilot-scheduler>`,
  styles: [`
    .controls {
      margin: 10px 0px;
      display: flex;
      align-items: center;
    }
    .controls input[type=range] {
      width: 80px;
      margin: 0px 5px;
    }
    .controls button {
      width: 25px;
      height: 25px;

      background-color: #3c78d8;
      color: white;
      border: 0;
      cursor: pointer;
    }
    .controls button:focus {
      outline: none;
    }
    #label {
      display: inline-block;
      margin-left: 10px;
    }

  `]
})
export class SchedulerComponent implements AfterViewInit, DoCheck {

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

  events: DayPilot.EventData[] = [];

  config: DayPilot.SchedulerConfig = {
    onTimeRangeSelected: args => {
      var dp = this.scheduler.control;
      DayPilot.Modal.prompt("Create a new event:", "Event 1").then(modal => {
        dp.clearSelection();
        if (modal.canceled) { return; }
        dp.events.add({
          start: args.start,
          end: args.end,
          id: DayPilot.guid(),
          resource: args.resource,
          text: modal.result
        });
      });
    },
    treeEnabled: true,
    zoom: 1,
    zoomLevels: [
      {
        name: "Year",
        properties: {
          scale: "Day",
          cellWidth: 40,
          timeHeaders: [{groupBy: "Year"}, {groupBy: "Month", format: "MMMM"}, {groupBy: "Day", format: "d"}],
          startDate: (args: ZoomLevelCallbackArgs) => { return args.date.firstDayOfYear(); },
          days: (args: ZoomLevelCallbackArgs) => { return args.date.daysInYear(); },
        }
      },
      {
        name: "Month",
        properties: {
          scale: "CellDuration",
          cellDuration: 720,
          cellWidth: 40,
          timeHeaders: [{groupBy: "Month"}, {groupBy: "Day", format: "ddd d"}, {groupBy: "Cell", format: "tt"}],
          startDate: (args: ZoomLevelCallbackArgs) => { return args.date.firstDayOfMonth(); },
          days: (args: ZoomLevelCallbackArgs) => { return args.date.daysInMonth();},
        }
      },
      {
        name: "Week",
        properties: {
          scale: "Hour",
          cellWidth: 40,
          timeHeaders: [{groupBy: "Month"}, {groupBy: "Day", format: "dddd d"}, {groupBy: "Hour"}],
          startDate: (args: ZoomLevelCallbackArgs) => { return args.date.firstDayOfWeek(); },
          days: (_args: ZoomLevelCallbackArgs) => { return 7; },
        }
      },
      {
        name: "Hour",
        properties: {
          scale: "CellDuration",
          cellDuration: 15,
          cellWidth: 40,
          timeHeaders: [{groupBy: "Day", format: "dddd MMMM d, yyyy"}, {groupBy: "Hour"}, {groupBy: "Cell"}],
          startDate: (args: ZoomLevelCallbackArgs) => { return args.date.getDatePart(); },
          days: (_args: ZoomLevelCallbackArgs) => { return 1; },
        }
      },
    ]

  };

  constructor(private ds: DataService) {
  }

  plus(): void {
    // checking boundaries
    const zoomLevels = this.config.zoomLevels as Array<any>;
    this.config.zoom = Math.min(this.config.zoom as number + 1, zoomLevels.length - 1);
  }

  minus(): void {
    // checking boundaries
    this.config.zoom = Math.max(this.config.zoom as number - 1, 0);
  }

  get currentZoomLevelName() {
    const zoomLevels = this.config.zoomLevels as Array<any>;
    const currentLevel = this.config.zoom as number;
    return zoomLevels[currentLevel].name;
  }

  ngDoCheck(): void {
  }

  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;
    });
  }

}


export interface ZoomLevelCallbackArgs {
  date: DayPilot.Date;
  level: number;
}

History

  • December 8, 2022: Angular 15, DayPilot Pro 2022.4.5475

  • December 15, 2020: Angular 11, DayPilot Pro 2020.4.4807, typed config object, array functions, cleanup

  • May 28, 2019: Initial release. Angular 7