Event Calender using Angular
In this article, we will walk through the steps to create a dynamic Event Calendar using Angular 17 standalone components. This calendar will allow users to add events to specific dates and view them interactively. We will utilize Angular's powerful features and best practices to build a clean and efficient application.
Project Preview

Prerequisites
Before diving into the implementation, ensure you have the following tools and libraries installed:
- Node.js(version 14 or later)
- Angular CLI (version 17 or later)
- Basic knowledge of Angular and Typescript
Approach
We will follow these steps to create our Event Calendar:
- Set up the Angular project
- Create standalone components for the calendar and event list
- Implement the event service
- Integrate the components and service to display and manage events
- Style the calendar for a user-friendly interface
Steps to Create the Application
Step 1: Set Up a New Angular Project
First, create a new Angular project using the Angular CLI:
ng new event-calendar
cd event-calendar
Note: Do not enable server side Rendering.
This will create a new Angular project named event-calendar and navigate into the project directory.
Step 2: Generate the calendar and event list components
Next, we'll generate the necessary components for our calendar. Run the following commands:
ng generate component components/calendar
ng generate component components/month-view
ng generate component components/event-list
Step 3: Implement the Event Service
ng generate service services/event
Dependencies
"dependencies": {
"@angular/animations": "^17.3.0",
"@angular/common": "^17.3.0",
"@angular/compiler": "^17.3.0",
"@angular/core": "^17.3.0",
"@angular/forms": "^17.3.0",
"@angular/platform-browser": "^17.3.0",
"@angular/platform-browser-dynamic": "^17.3.0",
"@angular/router": "^17.3.0",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.14.3"
}
Project Structure

Example
<!-- src/app/components/calendar/calendar.component.html -->
<div class="calendar-container">
<div class="header">
<button (click)="changeMonth(-1)">Previous</button>
<h2>{{ currentDate | date : "MMMM yyyy" }}</h2>
<button (click)="changeMonth(1)">Next</button>
</div>
<app-month-view [date]="currentDate"></app-month-view>
</div>
<!-- src/app/components/event-list/event-list.component.html -->
<div class="event-list-container">
<ul>
<h3 *ngFor="let event of events">
{{ event }}
<button class="remove-event-btn" (click)="onRemoveEvent(event)">x</button>
</h3>
</ul>
</div>
<!-- src/app/components/month-view/month-view.component.html -->
<div class="month-view-container">
<div class="day" *ngFor="let day of days" (click)="selectDate(day)">
<div *ngIf="checkEvent(day) >= 1; else noEvents">
<div>
<h3 style="color: rgb(248, 72, 72)">
{{ day | date : "EEEE, dd" }}
</h3>
</div>
<div style="display: flex; justify-content: center">
<button class="add-event-btn" (click)="addEvent(day)">+</button>
</div>
</div>
<ng-template #noEvents>
<div>
<div>
<h3>
{{ day | date : "EEEE, dd" }}
</h3>
</div>
<div style="display: flex; justify-content: center">
<button class="add-event-btn" (click)="addEvent(day)">+</button>
</div>
</div>
</ng-template>
</div>
</div>
<!-- Events section -->
<div class="events-container" *ngIf="selectedDate">
<h3>Events for {{ selectedDate | date : "EEEE, MMMM dd, yyyy" }}</h3>
<app-event-list [events]="events[selectedDate.toISOString().split('T')[0]] || []"
(removeEvent)="removeEvent(selectedDate, $event)"></app-event-list>
</div>
<!-- src/app/app.component.html -->
<app-calendar></app-calendar>
/* Write CSS Here */
/* src/app/components/calendar/calendar.component.css */
.calendar-container {
width: 100%;
max-width: 1100px;
margin: 0 auto;
padding: 20px;
border: 1px solid #ccc;
border-radius: 10px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
background-color: #e9e9e9;
padding: 20px;
border-radius: 40px;
}
.header button {
padding: 10px;
border: none;
background-color: #007bff;
color: white;
border-radius: 5px;
cursor: pointer;
}
.header button:hover {
background-color: #0056b3;
}
.header h2 {
margin: 0;
}
/* Write CSS Here */
/* src/app/components/event-list/event-list.component.css */
.event-list-container {
padding: 10px 0;
}
.event-list-container ul {
list-style: none;
padding: 0;
margin: 0;
}
.event-list-container h3 {
display: flex;
justify-content: space-between;
align-items: center;
padding: 5px;
border: 1px solid #ccc;
border-radius: 5px;
background-color: #258cf3;
margin-bottom: 5px;
color: white;
}
.remove-event-btn {
height: 25px;
width: 25px;
border: none;
background-color: #dc3545;
color: white;
border-radius: 5px;
cursor: pointer;
font-size: 1em;
}
.remove-event-btn:hover {
background-color: #c82333;
}
/* Write CSS Here */
/* src/app/components/month-view/month-view.component.css */
.month-view-container {
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 10px;
width: 100%;
margin-bottom: 20px;
}
.header-row {
display: grid;
grid-template-columns: repeat(7, 1fr);
width: 100%;
margin-bottom: 10px;
}
.day {
display: flex;
flex-direction: column;
align-items: center;
font: bold;
border: 1px solid #ccc;
border-radius: 5px;
padding: 10px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
cursor: pointer;
}
.events-container {
margin-top: 20px;
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
background-color: #e9e9e9;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
h3 {
margin-top: 0;
}
.add-event-btn {
height: 20px;
width: 100%;
border: none;
background-color: #33c90e;
color: white;
border-radius: 10px;
cursor: pointer;
font: bold;
display: none;
}
.add-event-btn:hover {
background-color: #218838;
}
.day:hover {
.add-event-btn {
display: block;
}
background-color: #e9e9e9;
color: white;
}
// src/app/components/calendar/calendar.component.ts
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MonthViewComponent } from '../month-view/month-view.component';
@Component({
selector: 'app-calendar',
standalone: true,
imports: [CommonModule, MonthViewComponent],
templateUrl: './calendar.component.html',
styleUrls: ['./calendar.component.css'],
})
export class CalendarComponent {
currentDate = new Date();
changeMonth(offset: number) {
this.currentDate = new Date(
this.currentDate.setMonth(this.currentDate.getMonth() + offset)
);
}
}
// src/app/components/event-list/event-list.component.ts
import { Component, Input, Output, EventEmitter } from '@angular/core';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-event-list',
standalone: true,
imports: [CommonModule],
templateUrl: './event-list.component.html',
styleUrls: ['./event-list.component.css'],
})
export class EventListComponent {
@Input() events!: string[];
@Output() removeEvent = new EventEmitter<string>();
onRemoveEvent(event: string) {
this.removeEvent.emit(event);
}
}
//src/app/components/month-view/month-view.component.ts
import { Component, Input, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { EventListComponent } from '../event-list/event-list.component';
import { EventService } from '../../services/event.service';
@Component({
selector: 'app-month-view',
standalone: true,
imports: [CommonModule, EventListComponent],
templateUrl: './month-view.component.html',
styleUrls: ['./month-view.component.css'],
})
export class MonthViewComponent implements OnInit {
@Input() date!: Date;
days: Date[] = [];
events: { [key: string]: string[] } = {};
selectedDate: Date | null = null;
constructor(private eventService: EventService) { }
ngOnInit() {
this.days = this.getDaysInMonth();
this.loadEvents();
}
ngOnChanges() {
this.days = this.getDaysInMonth();
this.loadEvents();
}
selectDate(day: Date) {
this.selectedDate = day;
}
getDaysInMonth(): Date[] {
const days = [];
const year = this.date.getFullYear();
const month = this.date.getMonth();
const numDays = new Date(year, month + 1, 0).getDate();
for (let i = 1; i <= numDays; i++) {
days.push(new Date(year, month, i));
}
return days;
}
loadEvents() {
this.days.forEach((day) => {
const dateString = day.toISOString().split('T')[0];
this.events[dateString] = this.eventService.getEvents(dateString);
});
}
addEvent(day: Date) {
const event = prompt('Enter event:');
if (event) {
const dateString = day.toISOString().split('T')[0];
this.eventService.addEvent(dateString, event);
this.loadEvents();
}
}
removeEvent(day: Date, event: string) {
const dateString = day.toISOString().split('T')[0];
this.eventService.removeEvent(dateString, event);
this.loadEvents();
}
checkEvent(day: Date) {
const dateString = day.toISOString().split('T')[0];
const arr = this.eventService.getEvents(dateString);
return arr.length;
}
}
//src/app/services/event.service.ts
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class EventService {
private storageKey = 'events';
constructor() {
this.loadEvents();
}
private events: { [key: string]: string[] } = {};
private saveEvents() {
localStorage.setItem(this.storageKey, JSON.stringify(this.events));
}
private loadEvents() {
const storedEvents = localStorage.getItem(this.storageKey);
if (storedEvents) {
this.events = JSON.parse(storedEvents);
}
}
addEvent(date: string, event: string) {
if (!this.events[date]) {
this.events[date] = [];
}
this.events[date].push(event);
this.saveEvents();
}
getEvents(date: string): string[] {
return this.events[date] || [];
}
removeEvent(date: string, event: string) {
if (this.events[date]) {
this.events[date] = this.events[date].filter((e) => e !== event);
if (this.events[date].length === 0) {
delete this.events[date];
}
this.saveEvents();
}
}
}
// src/app/app.component.ts
import { Component } from '@angular/core';
import { CalendarComponent } from './components/calendar/calendar.component';
@Component({
selector: 'app-root',
standalone: true,
imports: [CalendarComponent],
template: '<app-calendar></app-calendar>',
styleUrls: ['./app.component.css'],
})
export class AppComponent { }
Steps to Run the Application
ng serve --open
Open your browser and navigate to http://localhost:4200