Table of Contents

Introduction

Angular Calendar is a component that allows a user to drag and create events in a calendar and also shows events on the month, week, or day view. In this tutorial, we will learn how to implement Angular Calendar component with the feature to drag and create events.

Tutorial Goal: Implement Angular Calendar Component

Before building the demo let’s watch the below video and see what are we going to build.

Create an Angular CLI Project

To install Angular CLI run the below-given command:

Copy Text
npm install -g @angular/cli

Run the following command for creating a new Angular Project:

Copy Text
ng new angular-calendar-demo
cd angular-calendar-demo

Install Angular Calendar

Install angular-calendar & date-fns through npm:

Copy Text
npm install angular-calendar date-fns

Adding Angular Calendar to your Application

Import the CSS in global styles of the project i.e. src/styles.css.

Copy Text
@import "../node_modules/angular-calendar/css/angular-calendar.css";

Import angular-calendar & date-fns in the app.module.ts file as shown in the following code.

// app.module.ts

Copy Text
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { CalendarModule, DateAdapter } from 'angular-calendar';
import { adapterFactory } from 'angular-calendar/date-adapters/date-fns';
import { DragCompComponent } from './view/drag-comp/drag-comp.component';

@NgModule({
  declarations: [AppComponent, DragCompComponent],
  imports: [
    BrowserModule,
    AppRoutingModule,
    CalendarModule.forRoot({
      provide: DateAdapter,
      useFactory: adapterFactory,
    }),
  ],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}

Create a New Component

Copy Text
ng generate component drag-comp

Use the following TypeScript code: Business Logic

// drag-comp.component.ts

Copy Text
import { ChangeDetectorRef } from '@angular/core';
import {
  ChangeDetectionStrategy,
  Component,
  OnInit,
  ViewEncapsulation,
} from '@angular/core';
import { CalendarEvent, CalendarEventTitleFormatter } from 'angular-calendar';
import { WeekViewHourSegment } from 'calendar-utils';
import { addDays, addMinutes, endOfWeek } from 'date-fns';
import { fromEvent } from 'rxjs';
import { finalize, takeUntil } from 'rxjs/operators';

function floorToNearest(amount: number, precision: number) {
  return Math.floor(amount / precision) * precision;
}


function ceilToNearest(amount: number, precision: number) {
  return Math.ceil(amount / precision) * precision;
}

@Component({
  selector: 'app-drag-comp',
  templateUrl: './drag-comp.component.html',
  styleUrls: ['./drag-comp.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
  providers: [
    {
      provide: CalendarEventTitleFormatter,
    },
  ],
  encapsulation: ViewEncapsulation.None,
})
export class DragCompComponent implements OnInit {
  viewDate = new Date();
  weekStartsOn: 0 = 0;
  dragToCreateActive = false;
  events: CalendarEvent[] = [];
  days: any[] = [];
  slots: any[] = [];
  constructor(private cdr: ChangeDetectorRef) {}

  ngOnInit(): void {
    this.initDays();
  }

  initDays() {
    this.days = [
      'Sunday',
      'Monday',
      'Tuesday',
      'Wednesday',
      'Thursday',
      'Friday',
      'Saturday',
    ];
    for (let i = 0; i < this.days.length; i++) {
      let a = { day: this.days[i], time: [] };
      this.slots.push(a);
    }
  }

  startDragToCreate(
    segment: WeekViewHourSegment,
    mouseDownEvent: MouseEvent,
    segmentElement: HTMLElement
  ) {
    const dragToSelectEvent: CalendarEvent = {
      id: this.events.length,
      title: 'New slot',
      start: segment.date,
      meta: {
        tmpEvent: true,
      },
      actions: [
        {
          label: '',
          onClick: ({ event }: { event: CalendarEvent }): void => {
            this.events = this.events.filter((iEvent) => iEvent !== event);
            this.removeSlot(event.id);
          },
        },
      ],
    };
    this.events = [...this.events, dragToSelectEvent];

    const segmentPosition = segmentElement.getBoundingClientRect();
    this.dragToCreateActive = true;
    const endOfView = endOfWeek(this.viewDate, {
      weekStartsOn: this.weekStartsOn,
    });

    fromEvent(document, 'mousemove')
      .pipe(
        finalize(() => {
          delete dragToSelectEvent.meta.tmpEvent;
          this.dragToCreateActive = false;
          this.refresh();
        }),
        takeUntil(fromEvent(document, 'mouseup'))
      )
      .subscribe((mouseMoveEvent: MouseEvent) => {
        const minutesDiff = ceilToNearest(
          mouseMoveEvent.clientY - segmentPosition.top,
          30
        );

        const daysDiff =
          floorToNearest(
            mouseMoveEvent.clientX - segmentPosition.left,
            segmentPosition.width
          ) / segmentPosition.width;

        const newEnd = addDays(addMinutes(segment.date, minutesDiff), daysDiff);
        if (newEnd > segment.date && newEnd < endOfView) {
          dragToSelectEvent.end = newEnd;
        }
        this.refresh();
      });
  }

  private refresh() {
    this.events = [...this.events];
    this.cdr.detectChanges();
    this.getSlots();
  }

  convertTime(t) {
    return new Date(t).toTimeString();
  }

  convertDay(d) {
    return new Date(d).toLocaleString('en-us', {
      weekday: 'long',
    });
  }

  getSlots() {
    this.slots.map((day, i) => {
      this.slots[i].time = [];
      this.events.forEach((e) => {
        if (day.day == this.convertDay(e.start)) {
          this.slots[i].time.push({
            startTime: e.start,
            endTime: e.end,
            id: e.id,
          });
        }
      });
    });
  }

  removeSlot(id) {
    for (let j = 0; j < this.slots.length; j++) {
      this.slots[j].time = this.slots[j].time.filter((t) => t.id !== id);
    }
  }
}

Use the following HTML code: User Interface

// drag-comp.component.html

Copy Text
<div class="container">
  <ng-template #weekViewHourSegmentTemplate 
     let-segment="segment" vent 
     let-locale="locale" 
     let-segmentHeight="segmentHeight" 
     let-isTimeLabel="isTimeLabel"
   >
    <div #segmentElement 
       class="cal-hour-segment" 
       [style.height.px]="segmentHeight"   
       [class.cal-hour-start]="segment.isStart"
       [class.cal-after-hour-start]="!segment.isStart" 
       [ngClass]="segment.cssClass"
       (mousedown)="startDragToCreate(segment, $event, segmentElement)">
        <div class="cal-time" *ngIf="isTimeLabel">
            {{ segment.date | calendarDate:'weekViewHour':locale }}
        </div>
    </div>
</ng-template>
</div>

<mwl-calendar-week-view 
    [viewDate]="viewDate" [events]="events"
    [hourSegmentTemplate]="weekViewHourSegmentTemplate" 
    [weekStartsOn]="weekStartsOn">
</mwl-calendar-week-view>

<hr>
<div>Selected Slots</div>
<pre>{{ slots | json }}</pre>

Run the Application

Copy Text
ng serve

As we are almost done with the implementation of the Angular Calendar let’s complete the process with the final step, to run the application.

Then, open the following URL in your browser: http://localhost:4200

Github Repository: Angular Calendar Source Code

You can visit the source code and clone the github repository to play around with the code.

Conclusion

So this was all about how to implement Angular Calendar Component in your Angular application. I hope now you can easily create your events and play around with the feature. The Angular Calendar component does provide various useful features we will discuss that surely as well! Want to learn more about Angular? Visit the Angular tutorials page! No doubt, you will love the tutorials that our team has been curated for you! Reach out to us in case you have any suggestions/queries/feedback. We’re always delighted to answer!

Want to Implement Angular Calendar Component for Drag & Create Events?

BOOK A 30 MIN EXPERT CALL TO KNOW HOW

Build Your Agile Team

Hire Skilled Developer From Us

Subscribe for
weekly updates

newsletter

What Makes Bacancy Stand Out?

  • Technical Subject Matter Experts
  • 2500+ Projects Completed
  • 90% Client Retention Ratio
  • 12+ Years of Experience

Our developers primarily focus on navigating client's requirements with precision. Besides, we develop and innovate to deliver only the best solutions to our clients.

get in touch
[email protected]

Your Success Is Guaranteed !

We accelerate the release of digital product and guaranteed their success

We Use Slack, Jira & GitHub for Accurate Deployment and Effective Communication.

How Can We Help You?