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

Apache License 2.0

Angular Weekly Calendar Component

angular calendar open source date switching week

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?

angular calendar open source date picker

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?

angular calendar open source date switching next previous today

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

}