作業環境
- Windows10
- Angular8.23
ライブラリのインストール
# 必要なライブラリのインストール $ ng add angular-calendar $ npm install --save flatpickr angularx-flatpickr $ npm install --save @ng-bootstrap/ng-bootstrap@5.0.0 $ npm install --save @angular/flex-layout@8.0.0-beta.27 # コンポーネントを追加 $ ng generate component ngcalendar-sample
ng-bootstrapとflex-layoutはangularのバージョン違いでエラーよく吐くので、最新版以外を使用する場合は@でバージョン指定する必要があります。
<app-routing.module.ts>
import { NgModule } from "@angular/core";
import { Routes, RouterModule } from "@angular/router";
~~~~
~~~~
import { NgcalendarSampleComponent } from "./ngcalendar-sample/ngcalendar-sample.component"; //これを追加!
const routes: Routes = [
~~~~
~~~~
{ path: "angular-calendar-sample", component: NgcalendarSampleComponent },//これを追加!
];
<app.module.ts>
import { BrowserModule } from "@angular/platform-browser";
import { NgModule } from "@angular/core";
import { AppRoutingModule } from "./app-routing.module";
import { AppComponent } from "./app.component";
~~~~
~~~~
import { NgcalendarSampleComponent } from './ngcalendar-sample/ngcalendar-sample.component'; //これを追加!
import { CalendarModule, DateAdapter } from 'angular-calendar'; //これを追加!
import { adapterFactory } from 'angular-calendar/date-adapters/date-fns'; //これを追加!
import { NgbModalModule } from "@ng-bootstrap/ng-bootstrap";//これを追加!
import { FlatpickrModule } from "angularx-flatpickr";//これを追加!
import { FormsModule } from "@angular/forms";//これを追加!
import { CommonModule } from "@angular/common";//これを追加!
import { MatButtonToggleModule } from "@angular/material/button-toggle";//これを追加!
import { FlexLayoutModule } from "@angular/flex-layout";//これを追加!
@NgModule({
declarations: [
AppComponent,
~~~~
~~~~
NgcalendarSampleComponent,
],
imports: [
BrowserModule,
AppRoutingModule,
BrowserAnimationsModule,
~~~~
~~~~
CalendarModule.forRoot({ provide: DateAdapter, useFactory: adapterFactory }), //これを追加!
CommonModule,//これを追加!
FormsModule,//これを追加!
FlatpickrModule.forRoot(),//これを追加!
NgbModalModule,//これを追加!
MatButtonToggleModule,//これを追加!
FlexLayoutModule//これを追加!
],
~~~~
~~~~
providers: [],
bootstrap: [AppComponent],
exports: [NgcalendarSampleComponent],//これを追加!
})
export class AppModule {}
<styles.scss>
~~~~ ~~~~ @import 'flatpickr/dist/flatpickr.css'
スケジューラー画面の実装
<ngcalendar-sample.component.ts>
import {
Component,
ChangeDetectionStrategy,
ViewChild,
TemplateRef,
OnInit,
} from "@angular/core";
import {
startOfDay,
endOfDay,
subDays,
addDays,
endOfMonth,
isSameDay,
isSameMonth,
addHours,
} from "date-fns";
import { Subject } from "rxjs";
import { NgbModal } from "@ng-bootstrap/ng-bootstrap";
import {
CalendarEvent,
CalendarEventAction,
CalendarEventTimesChangedEvent,
CalendarView,
} from "angular-calendar";
const colors: any = {
red: {
primary: "#ad2121",
secondary: "#FAE3E3",
},
blue: {
primary: "#1e90ff",
secondary: "#D1E8FF",
},
yellow: {
primary: "#e3bc08",
secondary: "#FDF1BA",
},
};
@Component({
selector: "app-ngcalendar-sample",
templateUrl: "./ngcalendar-sample.component.html",
styleUrls: ["./ngcalendar-sample.component.css"],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class NgcalendarSampleComponent implements OnInit {
@ViewChild("modalContent", { static: true }) modalContent: TemplateRef;
view: CalendarView = CalendarView.Month;
CalendarView = CalendarView;
viewDate: Date = new Date();
modalData: {
action: string;
event: CalendarEvent;
};
actions: CalendarEventAction[] = [
{
label: '',
a11yLabel: "Edit",
onClick: ({ event }: { event: CalendarEvent }): void => {
this.handleEvent("Edited", event);
},
},
{
label: '',
a11yLabel: "Delete",
onClick: ({ event }: { event: CalendarEvent }): void => {
this.events = this.events.filter((iEvent) => iEvent !== event);
this.handleEvent("Deleted", event);
},
},
];
refresh: Subject = new Subject();
events: CalendarEvent[] = [
{
start: subDays(startOfDay(new Date()), 1),
end: addDays(new Date(), 1),
title: "A 3 day event",
color: colors.red,
actions: this.actions,
allDay: true,
resizable: {
beforeStart: true,
afterEnd: true,
},
draggable: true,
},
{
start: startOfDay(new Date()),
title: "An event with no end date",
color: colors.yellow,
actions: this.actions,
},
{
start: subDays(endOfMonth(new Date()), 3),
end: addDays(endOfMonth(new Date()), 3),
title: "A long event that spans 2 months",
color: colors.blue,
allDay: true,
},
{
start: addHours(startOfDay(new Date()), 2),
end: addHours(new Date(), 2),
title: "A draggable and resizable event",
color: colors.yellow,
allDay: true,
// actions: this.actions,
// resizable: {
// beforeStart: true,
// afterEnd: true,
// },
// draggable: true,
},
];
activeDayIsOpen: boolean = true;
constructor(private modal: NgbModal) {}
dayClicked({ date, events }: { date: Date; events: CalendarEvent[] }): void {
if (isSameMonth(date, this.viewDate)) {
if (
(isSameDay(this.viewDate, date) && this.activeDayIsOpen === true) ||
events.length === 0
) {
this.activeDayIsOpen = false;
} else {
this.activeDayIsOpen = true;
}
this.viewDate = date;
}
}
eventTimesChanged({
event,
newStart,
newEnd,
}: CalendarEventTimesChangedEvent): void {
this.events = this.events.map((iEvent) => {
if (iEvent === event) {
return {
...event,
start: newStart,
end: newEnd,
};
}
return iEvent;
});
this.handleEvent("Dropped or resized", event);
}
handleEvent(action: string, event: CalendarEvent): void {
this.modalData = { event, action };
this.modal.open(this.modalContent, { size: "lg" });
}
addEvent(): void {
this.events = [
...this.events,
{
title: "New event",
start: startOfDay(new Date()),
end: endOfDay(new Date()),
color: colors.red,
draggable: true,
resizable: {
beforeStart: true,
afterEnd: true,
},
},
];
}
deleteEvent(eventToDelete: CalendarEvent) {
this.events = this.events.filter((event) => event !== eventToDelete);
}
setView(view: CalendarView) {
this.view = view;
}
closeOpenMonthViewDay() {
this.activeDayIsOpen = false;
}
ngOnInit() {}
}
<ngcalendar-sample.component.html>
<div class="container" fxLayout="row">
<!-- <div class="col-md-4">
<div class="btn-group">
<div
class="btn btn-primary"
mwlCalendarPreviousView
[view]="view"
[(viewDate)]="viewDate"
(viewDateChange)="closeOpenMonthViewDay()"
>
Previous
</div>
<div
class="btn btn-outline-secondary"
mwlCalendarToday
[(viewDate)]="viewDate"
>
Today
</div>
<div
class="btn btn-primary"
mwlCalendarNextView
[view]="view"
[(viewDate)]="viewDate"
(viewDateChange)="closeOpenMonthViewDay()"
>
Next
</div>
</div>
</div> -->
<div class="item item1" fxFlex="40">
<mat-button-toggle-group #group="matButtonToggleGroup">
<mat-button-toggle value="left" aria-label="Text align left">
<div class="btn btn-primary" mwlCalendarPreviousView [view]="view" [(viewDate)]="viewDate"
(viewDateChange)="closeOpenMonthViewDay()">
Previous
</div>
</mat-button-toggle>
<mat-button-toggle value="center" aria-label="Text align center">
<div class="btn btn-outline-secondary" mwlCalendarToday [(viewDate)]="viewDate">
Today
</div>
</mat-button-toggle>
<mat-button-toggle value="right" aria-label="Text align right">
<div class="btn btn-primary" mwlCalendarNextView [view]="view" [(viewDate)]="viewDate"
(viewDateChange)="closeOpenMonthViewDay()">
Next
</div>
</mat-button-toggle>
</mat-button-toggle-group>
</div>
<div class="item item2" fxFlex="20">
<strong> {{ viewDate | calendarDate: view + "ViewTitle":"en" }} </strong>
</div>
<div class="item item3" fxFlex="40">
<mat-button-toggle-group #group="matButtonToggleGroup">
<mat-button-toggle value="left" aria-label="Text align left">
<div class="btn btn-primary" (click)="setView(CalendarView.Month)" [class.active]="view === CalendarView.Month">
Month
</div>
</mat-button-toggle>
<mat-button-toggle value="center" aria-label="Text align center">
<div class="btn btn-primary" (click)="setView(CalendarView.Week)" [class.active]="view === CalendarView.Week">
Week
</div>
</mat-button-toggle>
<mat-button-toggle value="right" aria-label="Text align right">
<div class="btn btn-primary" (click)="setView(CalendarView.Day)" [class.active]="view === CalendarView.Day">
Day
</div>
</mat-button-toggle>
</mat-button-toggle-group>
<!-- <div class="btn-group">
<div
class="btn btn-primary"
(click)="setView(CalendarView.Month)"
[class.active]="view === CalendarView.Month"
>
Month
</div>
<div
class="btn btn-primary"
(click)="setView(CalendarView.Week)"
[class.active]="view === CalendarView.Week"
>
Week
</div>
<div
class="btn btn-primary"
(click)="setView(CalendarView.Day)"
[class.active]="view === CalendarView.Day"
>
Day
</div>
</div> -->
</div>
</div>
<br />
<div [ngSwitch]="view">
<mwl-calendar-month-view *ngSwitchCase="CalendarView.Month" [viewDate]="viewDate" [events]="events"
[refresh]="refresh" [activeDayIsOpen]="activeDayIsOpen" (dayClicked)="dayClicked($event.day)"
(eventClicked)="handleEvent('Clicked', $event.event)" (eventTimesChanged)="eventTimesChanged($event)">
</mwl-calendar-month-view>
<mwl-calendar-week-view *ngSwitchCase="CalendarView.Week" [viewDate]="viewDate" [events]="events" [refresh]="refresh"
(eventClicked)="handleEvent('Clicked', $event.event)" (eventTimesChanged)="eventTimesChanged($event)">
</mwl-calendar-week-view>
<mwl-calendar-day-view *ngSwitchCase="CalendarView.Day" [viewDate]="viewDate" [events]="events" [refresh]="refresh"
(eventClicked)="handleEvent('Clicked', $event.event)" (eventTimesChanged)="eventTimesChanged($event)">
</mwl-calendar-day-view>
</div>
<!-- Everything you see below is just for the demo, you don't need to include it in your app -->
<br /><br /><br />
<h3>
Edit events
<button class="btn btn-primary pull-right" (click)="addEvent()">
Add new
</button>
<div class="clearfix"></div>
</h3>
<div class="table-responsive">
<table class="table table-bordered">
<thead>
<tr>
<th>Title</th>
<th>Primary color</th>
<th>Secondary color</th>
<th>Starts at</th>
<th>Ends at</th>
<th>Remove</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let event of events">
<td>
<input type="text" class="form-control" [(ngModel)]="event.title" (keyup)="refresh.next()" />
</td>
<td>
<input type="color" [(ngModel)]="event.color.primary" (change)="refresh.next()" />
</td>
<td>
<input type="color" [(ngModel)]="event.color.secondary" (change)="refresh.next()" />
</td>
<td>
<input class="form-control" type="text" mwlFlatpickr [(ngModel)]="event.start"
(ngModelChange)="refresh.next()" [altInput]="true" [convertModelValue]="true" [enableTime]="true"
dateFormat="Y-m-dTH:i" altFormat="F j, Y H:i" placeholder="Not set" />
</td>
<td>
<input class="form-control" type="text" mwlFlatpickr [(ngModel)]="event.end" (ngModelChange)="refresh.next()"
[altInput]="true" [convertModelValue]="true" [enableTime]="true" dateFormat="Y-m-dTH:i"
altFormat="F j, Y H:i" placeholder="Not set" />
</td>
<td>
<button class="btn btn-danger" (click)="deleteEvent(event)">
Delete
</button>
</td>
</tr>
</tbody>
</table>
</div>
<ng-template #modalContent let-close="close">
<div class="modal-header">
<h5 class="modal-title">Event action occurred</h5>
<button type="button" class="close" (click)="close()">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<div>
Action:
<pre>{{ modalData?.action }}</pre>
</div>
<div>
Event:
<pre>{{ modalData?.event | json }}</pre>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-secondary" (click)="close()">
OK
</button>
</div>
</ng-template>
http://localhost:4200/angular-calendar-sampleにアクセスすると以下のような画面が表示されます。

weekタブをクリックすると以下のように週単位のスケジューラーが表示されます。

dayタブをクリックすると以下のように日にち単位のスケジューラーが表示されます

fullcalendarは週/日のスケジューラーが有料なので、それを無料で提供しているのはangular-calendarの強みですね。日本語設定とかが分かりにくいですが
関連記事
・【Angular】fullcalendarでGoogleカレンダーライクな画面を実装する
・AngularによるWebアプリ開発②~Angular Material をインストールしてマテリアルデザインを実装する
オススメ参考書
コメント