Overview
Define custom zoom levels for the Angular Scheduler component
Switching the zoom level using HTML5 range control and plus/minus buttons
Includes a trial version of DayPilot Pro for JavaScript (see also License below)
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 scale, timeHeaders, startDate, 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 viewportargs.level
- the new zoom level object (the parentzoomLevel
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
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
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