Features
The Angular Scheduler from DayPilot Pro for JavaScript supports several ways of showing event details on hover
The built-in bubble tooltip can show static HTML, dynamic HTML built on the client side, dynamic HTML built on the server side or a dynamic Angular component
The tutorial ncludes a trial version of DayPilot Pro for JavaScript (see License below)
License
Licensed for testing and evaluation purposes. Please see the license agreement included in the sample project. You can use the source code of the tutorial if you are a licensed user of DayPilot Pro for JavaScript.
Introduction
For introduction to using the Angular Scheduler component please see the basic tutorial:
Bubble Tooltip for Scheduler Events
The Angular Scheduler component uses a built-in DayPilot.Bubble class to display a custom tooltip for Scheduler events.
The easiest way to display a static event tooltip is to add bubbleHtml
property to the event data object:
events: DayPilot.EventData[] = [
{
id: '1',
resource: 'R3',
start: '2022-06-03',
end: '2022-06-08',
text: 'Scheduler Event 1',
bubbleHtml: "<div style='font-weight:bold'>Event Details</div><div>Scheduler Event 1</div>"
}
// ...
];
Loading Tooltip HTML from the Server
Instead of the static tooltip HTML, you can use onLoad event handler of the Bubble object to define custom HTML on the fly.
Synchronous Event Handler
You can return the HTML synchronously using args.html
property. You can use this approach if the event object has all the data you need to build the HTML.
config: DayPilot.SchedulerConfig = {
// ...
bubble: new DayPilot.Bubble({
onLoad: args => {
const event = args.source;
args.html = "<div style='font-weight:bold'>Event Details</div><div>" + event.text() + "</div><div>Starting on " + event.start().toString("MMMM d, yyyy") + "</div>";
}
})
};
Asynchronous Event Handler
If you need to load additional data from the server you can activate asynchronous loading. The Scheduler will wait with displaying the tooltip until you call args.loaded()
method.
You can use the asynchronous call to load more data from the server using an HTTP call.
config: any = {
// ...
bubble: new DayPilot.Bubble({
onLoad: args => {
args.async = true;
const event = args.source;
this.http.get("/getEventDetails/" + event.id()).subscribe(result => {
args.html = result;
args.loaded();
});
}
})
};
Dynamic Angular Component
Instead of supplying the tooltip HTML you can also insert an Angular component into the bubble tooltip. You need to use DayPilot.Bubble.onDomAdd
event handler to supply a custom DOM element that will be added to the bubble body.
First, you need to create the component dynamically:
const component: ComponentRef<TooltipComponent> = this.viewContainerRef.createComponent(TooltipComponent);
Then set the instance properties. We want to pass the Scheduler reference (DayPilot.Scheduler object) and the event reference (DayPilot.Event object) to the TooltipComponent
:
component.instance.scheduler = this.scheduler.control;
component.instance.event = e;
component.changeDetectorRef.detectChanges();
The steps above are used in createTooltipComponent()
helper method. It creates the component, initializes the data and returns a ComponentRef
instance:
createTooltipComponent(e: any): ComponentRef<TooltipComponent> {
const component: ComponentRef<TooltipComponent> = this.viewContainerRef.createComponent(TooltipComponent);
component.instance.scheduler = this.scheduler.control;
component.instance.event = e;
component.changeDetectorRef.detectChanges();
return component;
}
Use the onDomAdd
event handler of the DayPilot.Bubble
class to add the TooltipComponent
DOM element to the tooltip using args.element
property. This element will be used for the bubble content instead of the default HTML:
bubble: new DayPilot.Bubble({
onDomAdd: args => {
const component = this.createTooltipComponent(args.source);
args.element = component.location.nativeElement;
},
// ...
})
When the bubble tooltip is destroyed (and removed from the DOM) it calls onDomRemove
event handler. It's a good place for the cleanup code.
bubble: new DayPilot.Bubble({
onDomAdd: args => {
console.log("Creating TooltipComponent");
const component = this.createTooltipComponent(args.source);
args.element = component.location.nativeElement;
(<any>args).component = component;
},
onDomRemove: args => {
console.log("Destroying TooltipComponent");
(<any>args).component.destroy();
}
})
Full Source Code
scheduler.component.ts
import {
AfterViewInit,
Component,
ComponentRef,
ViewChild, ViewContainerRef
} from '@angular/core';
import {DayPilot, DayPilotSchedulerComponent} from 'daypilot-pro-angular';
import {DataService} from './data.service';
import {TooltipComponent} from "./tooltip.component";
@Component({
selector: 'scheduler-component',
template: `
<daypilot-scheduler [config]="config" [events]="events" #scheduler></daypilot-scheduler>`,
styles: [``]
})
export class SchedulerComponent implements AfterViewInit {
@ViewChild('scheduler')
scheduler!: DayPilotSchedulerComponent;
events: DayPilot.EventData[] = [];
config: DayPilot.SchedulerConfig = {
timeHeaders: [{"groupBy": "Month"}, {"groupBy": "Day", "format": "d"}],
scale: "Day",
days: 30,
startDate: "2022-06-01",
treeEnabled: true,
eventEndSpec: "Date",
onTimeRangeSelected: async (args) => {
const dp = this.scheduler.control;
const modal = await DayPilot.Modal.prompt("Create a new event:", "Event 1");
dp.clearSelection();
if (!modal.result) {
return;
}
dp.events.add({
start: args.start,
end: args.end,
id: DayPilot.guid(),
resource: args.resource,
text: modal.result
});
},
bubble: new DayPilot.Bubble({
onDomAdd: args => {
console.log("Creating TooltipComponent");
const component = this.createTooltipComponent(args.source);
args.element = component.location.nativeElement;
(<any>args).component = component;
},
onDomRemove: args => {
console.log("Destroying TooltipComponent");
(<any>args).component.destroy();
}
})
/*
bubble: new DayPilot.Bubble({
onLoad: args => {
const event = args.source;
args.html = "<div style='font-weight:bold'>Event Details</div><div>" + event.text() + "</div><div>Starting on " + event.start().toString("MMMM d, yyyy") + "</div>";
}
})
*/
};
constructor(private ds: DataService, private viewContainerRef: ViewContainerRef) {
}
ngAfterViewInit(): void {
this.ds.getResources().subscribe(result => this.config.resources = result);
const from = this.scheduler.control.visibleStart();
const to = this.scheduler.control.visibleEnd();
this.ds.getEvents(from, to).subscribe(result => {
this.events = result;
});
}
createTooltipComponent(e: any): ComponentRef<TooltipComponent> {
const component: ComponentRef<TooltipComponent> = this.viewContainerRef.createComponent(TooltipComponent);
component.instance.scheduler = this.scheduler.control;
component.instance.event = e;
component.changeDetectorRef.detectChanges();
return component;
}
}
tooltip.component.ts
import {Component} from '@angular/core';
import {DayPilot} from "daypilot-pro-angular";
@Component({
selector: 'tooltip-component',
template: `
<div class="main">
<div class="title">{{start}} - {{end}}</div>
<div class="text">{{text}}</div>
<div><a href="#" (click)="clickDelete($event)">Delete</a></div>
</div>
`,
styles: [`
.main {
width: 200px;
padding: 10px 0px;
}
.title {
font-weight: bold;
}
.text {
margin: 10px 0px;
}
a {
color: #000;
}
`]
})
export class TooltipComponent {
public event!: DayPilot.Event;
public scheduler!: DayPilot.Scheduler;
clickDelete(e: MouseEvent) {
e.preventDefault();
this.scheduler.events.remove(this.event);
}
get text() {
return this.event.text();
}
get start() {
return this.event.start().toString("MMMM d, yyyy");
}
get end() {
return this.event.end().toString("MMMM d, yyyy");
}
}
data.service.ts
import {Injectable} from '@angular/core';
import {DayPilot} from 'daypilot-pro-angular';
import {HttpClient} from '@angular/common/http';
import {Observable} from 'rxjs';
@Injectable()
export class DataService {
resources: any[] = [
{ name: 'Group A', id: 'GA', expanded: true, children: [
{ name: 'Resource 1', id: 'R1' },
{ name: 'Resource 2', id: 'R2' }
]},
{ name: 'Group B', id: 'GB', expanded: true, children: [
{ name: 'Resource 3', id: 'R3', unavailable: true},
{ name: 'Resource 4', id: 'R4'}
]}
];
events: any[] = [
{
id: '1',
resource: 'R2',
start: '2022-06-08',
end: '2022-06-12',
text: 'Scheduler Event 1',
barColor: '#e69138',
barBackcolor: '#fff',
},
{
id: '2',
resource: 'R3',
start: '2022-06-02',
end: '2022-06-05',
text: 'Scheduler Event 2',
barColor: '#6aa84f',
barBackcolor: '#fff'
},
{
id: '3',
resource: 'R3',
start: '2022-06-08',
end: '2022-06-09',
text: 'Scheduler Event 3',
barColor: '#3c78d8',
barBackcolor: '#fff'
}
];
constructor(private http: HttpClient) {
}
getEvents(from: DayPilot.Date, to: DayPilot.Date): Observable<any[]> {
// simulating an HTTP request
return new Observable(observer => {
setTimeout(() => {
observer.next(this.events);
}, 200);
});
// return this.http.get("/api/events?from=" + from.toString() + "&to=" + to.toString());
}
getResources(): Observable<any[]> {
// simulating an HTTP request
return new Observable(observer => {
setTimeout(() => {
observer.next(this.resources);
}, 200);
});
// return this.http.get("/api/resources");
}
}