Features
Angular Calendar component that displays events/appointments
Angular Date Picker component (Navigator) that allows changing the visible date
Next/Previous/Today buttons for quick date changing
Built using Angular Calendar components from the free and open-source DayPilot Lite for JavaScript package.
License
Angular Weekly Calendar Component
In the first step, we will add the calendar component to our Angular application. The calendar and date picker components are included in the @daypilot/daypilot-lite-angular package (DayPilot Lite open source library).
The <daypilot-calendar>
tag uses [config]
attribute to load the configuration and [events]
attribute to load event data.
HTML
<daypilot-calendar [config]="config" [events]="events" #calendar></daypilot-calendar>
TypeScript
import {Component, ViewChild, AfterViewInit} from "@angular/core";
import {DayPilot, DayPilotCalendarComponent} from "@daypilot/daypilot-lite-angular";
import {DataService} from "./data.service";
@Component({
selector: 'calendar-component',
templateUrl: './calendar.component.html',
styleUrls: ['./calendar.component.css']
})
export class CalendarComponent implements AfterViewInit {
events: DayPilot.EventData[] = [];
config: DayPilot.CalendarConfig = {
startDate: DayPilot.Date.today(),
viewType: "Week",
cellHeight: 30,
headerHeight: 30,
hourWidth: 60
};
constructor(private ds: DataService) {
}
ngAfterViewInit(): void {
var from = this.calendar.control.visibleStart();
var to = this.calendar.control.visibleEnd();
this.ds.getEvents(from, to).subscribe(result => {
this.events = result;
});
}
}
How to add the Angular date picker component?
Now we will add a date picker next to the calendar using DayPilot Navigator component.
<daypilot-navigator [config]="navigatorConfig"></daypilot-navigator>
The [config]
attribute points to an object with navigator configuration. We will use the config to set the select mode to week and to customize the appearance:
navigatorConfig: DayPilot.NavigatorConfig = {
showMonths: 3,
skipMonths: 3,
selectMode: "Week",
cellWidth: 30,
cellHeight: 30,
dayHeaderHeight: 30,
titleHeight: 30
};
The navigator component allows setting the selected date using date
property. It also uses dateChange
event for notification of selection changes. We will use the "banana in a box" Angular attribute syntax to set up a two-way binding between the date selected in the navigator component and the startDate
property of the calendar component.
<daypilot-navigator [config]="navigatorConfig" [(date)]="date" #navigator></daypilot-navigator>
The linked date
property provides access to config.startDate
:
get date(): DayPilot.Date {
return this.config.startDate as DayPilot.Date;
}
set date(value: DayPilot.Date) {
this.config.startDate = value;
}
Any change of the selected date will automatically update the calendar component. When the week displayed by the calendar changes, we need to load the event data for the new week. The calendar component provides a viewChange
event which is fired whenever the calendar is updated. We will use this event to detect view changes and load the appointments.
<daypilot-calendar [config]="config" [events]="events" #calendar (viewChange)="viewChange()"></daypilot-calendar>
The viewChange()
method loads the events using DataService.getEvents()
. This is the same code we had in ngAfterViewInit()
in the first version of our application:
viewChange(): void {
var from = this.calendar.control.visibleStart();
var to = this.calendar.control.visibleEnd();
this.ds.getEvents(from, to).subscribe(result => {
this.events = result;
});
}
This is how our Angular calendar component looks after integrating the date picker:
HTML
<div class="left">
<daypilot-navigator [config]="navigatorConfig" [(date)]="date" #navigator></daypilot-navigator>
</div>
<div class="main">
<daypilot-calendar [config]="config" [events]="events" #calendar (viewChange)="viewChange()"></daypilot-calendar>
</div>
TypeScript
import {Component, ViewChild, AfterViewInit} from "@angular/core";
import {DayPilot, DayPilotCalendarComponent, DayPilotNavigatorComponent} from "@daypilot/daypilot-lite-angular";
import {DataService} from "./data.service";
@Component({
selector: 'calendar-component',
templateUrl: './calendar.component.html',
styleUrls: ['./calendar.component.css']
})
export class CalendarComponent implements AfterViewInit {
@ViewChild("navigator") navigator!: DayPilotNavigatorComponent;
@ViewChild("calendar") calendar!: DayPilotCalendarComponent;
get date(): DayPilot.Date {
return this.config.startDate as DayPilot.Date;
}
set date(value: DayPilot.Date) {
this.config.startDate = value;
}
navigatorConfig: DayPilot.NavigatorConfig = {
showMonths: 3,
skipMonths: 3,
selectMode: "Week",
cellWidth: 30,
cellHeight: 30,
dayHeaderHeight: 30,
titleHeight: 30
};
events: DayPilot.EventData[] = [];
config: DayPilot.CalendarConfig = {
startDate: DayPilot.Date.today(),
viewType: "Week",
cellHeight: 30,
headerHeight: 30,
hourWidth: 60
};
constructor(private ds: DataService) {
}
ngAfterViewInit(): void {
}
viewChange(): void {
var from = this.calendar.control.visibleStart();
var to = this.calendar.control.visibleEnd();
this.ds.getEvents(from, to).subscribe(result => {
this.events = result;
});
}
}
How to add “Next”, “Today” and “Previous” buttons?
In the final step, we will add shortcut buttons for quick date changes. These buttons will directly modify config.startDate
property. Angular will automatically detect this change and update the calendar. The calendar will fire the viewChange
event which loads the data using the viewChange()
method that we already added.
HTML
<div class="left">
<daypilot-navigator [config]="navigatorConfig" [(date)]="date" #navigator></daypilot-navigator>
</div>
<div class="main">
<div class="buttons">
<a href="#" (click)="navigatePrevious($event)">Previous</a>
<a href="#" (click)="navigateToday($event)">Today</a>
<a href="#" (click)="navigateNext($event)">Next</a>
</div>
<daypilot-calendar [config]="config" [events]="events" #calendar (viewChange)="viewChange()"></daypilot-calendar>
</div>
TypeScript
import {Component, ViewChild, AfterViewInit} from "@angular/core";
import {DayPilot, DayPilotCalendarComponent, DayPilotNavigatorComponent} from "@daypilot/daypilot-lite-angular";
import {DataService} from "./data.service";
@Component({
selector: 'calendar-component',
templateUrl: './calendar.component.html',
styleUrls: ['./calendar.component.css']
})
export class CalendarComponent implements AfterViewInit {
// ...
navigatePrevious(event): void {
event.preventDefault();
this.config.startDate = (this.config.startDate as DayPilot.Date).addDays(-7);
}
navigateNext(event): void {
event.preventDefault();
this.config.startDate = (this.config.startDate as DayPilot.Date).addDays(7);
}
navigateToday(event): void {
event.preventDefault();
this.config.startDate = DayPilot.Date.today();
}
}
Full Source Code
calendar.component.css
:host ::ng-deep .calendar_default_colheader_inner {
padding: 5px;
}
:host ::ng-deep .calendar_default_rowheader_inner {
padding: 5px;
}
.wrap {
display: flex;
}
.left {
margin-right: 10px;
}
.main {
flex-grow: 1;
}
.buttons {
margin-bottom: 10px;
}
.buttons a {
display: inline-block;
text-align: center;
background-color: #3c78d8;
border: 1px solid #1155cc;
color: #fff;
padding: 6px 20px;
border-radius: 2px;
cursor: pointer;
margin-right: 5px;
width: 80px;
text-decoration: none;
}
calendar.component.html
<div class="left">
<daypilot-navigator [config]="navigatorConfig" [(date)]="date" #navigator></daypilot-navigator>
</div>
<div class="main">
<div class="buttons">
<a href="#" (click)="navigatePrevious($event)">Previous</a>
<a href="#" (click)="navigateToday($event)">Today</a>
<a href="#" (click)="navigateNext($event)">Next</a>
</div>
<daypilot-calendar [config]="config" [events]="events" #calendar (viewChange)="viewChange()"></daypilot-calendar>
</div>
calendar.component.ts
import {Component, ViewChild, AfterViewInit} from "@angular/core";
import {DayPilot, DayPilotCalendarComponent, DayPilotNavigatorComponent} from "@daypilot/daypilot-lite-angular";
import {DataService} from "./data.service";
@Component({
selector: 'calendar-component',
templateUrl: './calendar.component.html',
styleUrls: ['./calendar.component.css']
})
export class CalendarComponent implements AfterViewInit {
@ViewChild("navigator") navigator!: DayPilotNavigatorComponent;
@ViewChild("calendar") calendar!: DayPilotCalendarComponent;
get date(): DayPilot.Date {
return this.config.startDate as DayPilot.Date;
}
set date(value: DayPilot.Date) {
this.config.startDate = value;
}
navigatorConfig: DayPilot.NavigatorConfig = {
showMonths: 3,
skipMonths: 3,
selectMode: "Week",
cellWidth: 30,
cellHeight: 30,
dayHeaderHeight: 30,
titleHeight: 30
};
events: DayPilot.EventData[] = [];
config: DayPilot.CalendarConfig = {
startDate: DayPilot.Date.today(),
viewType: "Week",
cellHeight: 30,
headerHeight: 30,
hourWidth: 60,
onTimeRangeSelected: async (args) => {
const modal = await DayPilot.Modal.prompt("Create a new event:", "Event 1");
const dp = args.control;
dp.clearSelection();
if (!modal.result) { return; }
dp.events.add({
start: args.start,
end: args.end,
id: DayPilot.guid(),
text: modal.result
});
}
};
constructor(private ds: DataService) {
}
ngAfterViewInit(): void {
}
viewChange(): void {
var from = this.calendar.control.visibleStart();
var to = this.calendar.control.visibleEnd();
console.log("viewChange(): " + from + " " + to);
this.ds.getEvents(from, to).subscribe(result => {
this.events = result;
});
}
navigatePrevious(event: MouseEvent): void {
event.preventDefault();
this.config.startDate = (this.config.startDate as DayPilot.Date).addDays(-7);
}
navigateNext(event: MouseEvent): void {
event.preventDefault();
this.config.startDate = (this.config.startDate as DayPilot.Date).addDays(7);
}
navigateToday(event: MouseEvent): void {
event.preventDefault();
this.config.startDate = DayPilot.Date.today();
}
}