Features
Built using Angular Resource Calendar component from the open-source DayPilot Lite for JavaScript scheduling library.
The resource calendar displays resources (people, rooms, tools) as columns (on the horizontal axis).
Time is displayed on the vertical axis (24 hours per column).
License
Apache License 2.0
Angular Resource Calendar Project
This project includes the minimum setup required for the Angular resource calendar to run, including the boilerplate code and module dependencies. You can use it as a starting point for your own implementation.
It includes the Angular calendar component from @daypilot/daypilot-lite-angular NPM package.
Initialization
Before running the Angular application, it's necessary to download the dependencies specified in package.json. Simply run npm install (Node.js with npm package manager is required):
npm install
When the dependencies are downloaded to node_modules you can run the application:
npm start
This will start a built-in web server on port 4200. You can open the application in the browser at http://localhost:4200.
Angular Project Structure
The Angular project structure is based on the default structure which is created by Angular CLI. We will minimize changes made to the generated files - all custom code can be found in src/app/calendar directory, encapsulated in a special CalendarModule.
The CalendarModule consists of three files:
data.service.ts
calendar.component.ts
calendar.module.ts
Resource Calendar Component (calendar.component.ts)
The resource calendar is built using DayPilot Angular event calendar component.
By default, the calendar component displays a traditional calendar with one or more days as columns (day, week, workweek views):
However, you can easily switch it to resources mode which can display custom columns:
The vertical axis displays time of day. By default, the full calendar grid displays 24 hours a day and the viewport is configured to display business hours.
Note: The Calendar component can only display resources on the horizontal axis (as columns). If you want to display resources on the vertical axis (as rows) you can use the Scheduler component from DayPilot Pro.
The calendar.component.ts file defines CalendarComponent class, which is a wrapper around the DayPilot calendar component. It adds configuration properties and custom event handlers.
calendar.component.ts
import {Component, ViewChild, AfterViewInit} from "@angular/core";
import {DayPilot, DayPilotCalendarComponent} from "@daypilot/daypilot-lite-angular";
import {DataService} from "./data.service";
import {forkJoin} from "rxjs";
@Component({
selector: 'calendar-component',
template: `<daypilot-calendar [config]="config" #calendar></daypilot-calendar>`,
styles: [``]
})
export class CalendarComponent implements AfterViewInit {
@ViewChild("calendar")
calendar!: DayPilotCalendarComponent;
config: DayPilot.CalendarConfig = {
viewType: "Resources",
startDate: new DayPilot.Date("2022-09-01"),
onTimeRangeSelected: async args => {
const modal = await DayPilot.Modal.prompt("Create a new event:", "Event 1");
const dp = this.calendar.control;
dp.clearSelection();
if (modal.canceled) {
return;
}
dp.events.add({
start: args.start,
end: args.end,
id: DayPilot.guid(),
text: modal.result,
resource: args.resource
});
}
};
constructor(private ds: DataService) {
}
ngAfterViewInit(): void {
const from = this.config.startDate as DayPilot.Date;
const to = from.addDays(1);
forkJoin([
this.ds.getResources(),
this.ds.getEvents(from, to)
]).subscribe(data => {
const options = {
columns: data[0],
events: data[1]
};
this.calendar.control.update(options);
});
}
}
You can see that the configuration object includes a viewType property. This is required to switch the calendar component to the resource calendar mode:
config: DayPilot.CalendarConfig = {
viewType: "Resources",
// ...
};
The column data are loaded using a special DataService class (which wraps all the data-access code) - we will get to this class later. The columns can also be defined statically using the columns property:
config: DayPilot.CalendarConfig = {
// ...
columns: [
{name: "Resource 1", id: "R1"},
{name: "Resource 2", id: "R2"},
{name: "Resource 3", id: "R3"},
{name: "Resource 4", id: "R4"},
{name: "Resource 5", id: "R5"},
{name: "Resource 6", id: "R6"},
{name: "Resource 7", id: "R7"},
{name: "Resource 8", id: "R8"}
];
// ...
};
We load the initial column and event data in the ngAfterViewInit()
method. The column data are provided by DataService.getResources()
method and the event data are provided by DataService.getEvents()
method.
Both methods return an Observable class. In order to avoid a double update of the resource calendar component, we merge the calls using forkJoin() and wait for results from both services. As soon as the column and event data are available, we update the calendar using a single update() call, specifying the new values using the options
parameter.
forkJoin([
this.ds.getResources(),
this.ds.getEvents(from, to)
]).subscribe(data => {
const options = {
columns: data[0],
events: data[1]
};
this.calendar.control.update(options);
});
The header of our resource calendar now looks like this:
Each column has its own ID which will be used to display the correct events (each column will only include events with a matching resource id).
Handling User Events
The CalendarComponent class defines an onTimeRangeSelected event handler. This event is fired when a user selects a time range - we will use the event handler to create a new event at the selected location.
config: DayPilot.CalendarConfig = {
// ...
onTimeRangeSelected: async args => {
const modal = await DayPilot.Modal.prompt("Create a new event:", "Event 1");
const dp = this.calendar.control;
dp.clearSelection();
if (modal.canceled) {
return;
}
dp.events.add({
start: args.start,
end: args.end,
id: DayPilot.guid(),
text: modal.result,
resource: args.resource
});
}
};
This implementation opens a modal dialog for entering an event text, and adds a new event. In a real application, you'd also want to add a call to the server to save the new event in a database.
You can define more event handlers to implement custom handling of other user actions, such as drag and drop event moving and resizing.
Loading Data (data.service.ts)
The ngAfterViewInit() method of our resource calendar component requests the column and event data using a special DataService class:
constructor(private ds: DataService) {}
ngAfterViewInit(): void {
const from = this.config.startDate as DayPilot.Date;
const to = from.addDays(1);
forkJoin([
this.ds.getResources(),
this.ds.getEvents(from, to)
]).subscribe(data => {
const options = {
columns: data[0],
events: data[1]
};
this.calendar.control.update(options);
});
}
The DataService class contains all data-loading code. Our implementation is static - it defines the columns and events using an array. In a real-world application, you'd want to change the getResources() and getEvents() method implementations to make an HTTP call to a server to get the data from a database.
data.service.ts
import {Injectable} from "@angular/core";
import {Observable} from "rxjs";
import {DayPilot} from "@daypilot/daypilot-lite-angular";
import {HttpClient} from "@angular/common/http";
@Injectable()
export class DataService {
events: any[] = [
{
id: 1,
start: "2022-09-01T13:00:00",
end: "2022-09-01T15:00:00",
text: "Event 1",
resource: "R1",
barColor: "#f1c232"
},
{
id: 2,
start: "2022-09-01T10:00:00",
end: "2022-09-01T12:00:00",
text: "Event 2",
resource: "R1"
}
];
resources: any[] = [
{name: "Resource 1", id: "R1"},
{name: "Resource 2", id: "R2"},
{name: "Resource 3", id: "R3"},
{name: "Resource 4", id: "R4"},
{name: "Resource 5", id: "R5"},
{name: "Resource 6", id: "R6"},
{name: "Resource 7", id: "R7"},
{name: "Resource 8", id: "R8"}
];
constructor(private http: HttpClient) {
}
getEvents(from: DayPilot.Date, to: DayPilot.Date): Observable<any[]> {
// simulating an HTTP request
return new Observable(observer => {
setTimeout(() => {
observer.next(this.events);
observer.complete();
}, 200);
});
// return this.http.get("/api/events?from=" + from.toString() + "&to=" + to.toString());
}
getResources(): Observable<any[]> {
// simulating an HTTP request
return new Observable(observer => {
setTimeout(() => {
observer.next(this.resources);
observer.complete();
}, 200);
});
// return this.http.get("/api/resources");
}
}
Changing the Date Displayed by the Resource Calendar
This Angular project uses a simple configuration that doesn’t define the date for individual columns. All columns will use the same date, as defined using the startDate property:
config: DayPilot.CalendarConfig = {
// ...
startDate: "2022-09-01",
columns: [
{name: "Resource 1", id: "R1"},
{name: "Resource 2", id: "R2"},
{name: "Resource 3", id: "R3"},
{name: "Resource 4", id: "R4"},
{name: "Resource 5", id: "R5"},
{name: "Resource 6", id: "R6"},
{name: "Resource 7", id: "R7"},
{name: "Resource 8", id: "R8"}
];
// ...
};
To learn how to integrate the resource calendar with a date picker that lets your users change the current date, please see the following tutorial:
It is also possible to define a custom date for each column of the resource calendar. You need to specify the start property for the column data:
config: DayPilot.CalendarConfig = {
// ...
startDate: "2022-09-01",
columns: [
{name: "Resource 1", id: "R1"},
{name: "Resource 1/next day", id: "R1", start: "2022-09-02},
{name: "Resource 2", id: "R2"},
];
// ...
};