Features
How to add a dynamically-created component to Angular Scheduler events.
Create an HTML element from a dynamic component
Inserting the component using
onBeforeEventDomAdd
event of the Scheduler componentComponent cleanup in
onBeforeEventDomRemove
event of the Scheduler componentIncludes a trial version of DayPilot Pro for JavaScript (see also 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.
Angular Scheduler Event Customization
The Angular Scheduler component offers several tools to customize the event content:
By default the event displays the value of the
text
property specified using the event data object.You can override the text with custom HTML using the
html
property of the event data object.You can customize the event HTML in onBeforeEventRender event handler. This event handler lets you create the HTML dynamically from the event properties (you don't have to create the HTML on the server and send it as part of the JSON string).
You can add dynamic elements called active areas at custom position inside the event element.
In addition to these options, you can also provide custom HTML element that will be used as event body. This tutorial shows how to create the element dynamically from an Angular component and insert it into the Scheduler event.
Creating an Angular Component Dynamically
Let's create a simple LinkComponent
which displays a hyperlink using <a>
element:
import {Component} from '@angular/core';
import {DayPilot} from "daypilot-pro-angular";
@Component({
selector: 'link-component',
template: `<a href="#" (click)="click($event)">{{text}}</a>`
})
export class LinkComponent {
public text: string = "link";
public data: any;
click(e: MouseEvent) {
e.preventDefault();
e.stopPropagation();
DayPilot.Modal.alert("Event id: " + this.data.id);
}
}
We will use createLinkComponent()
method to create a component instance dynamically.
import {AfterViewInit, Component, ComponentRef, ViewChild, ViewContainerRef} from '@angular/core';
import {DayPilot, DayPilotSchedulerComponent} from 'daypilot-pro-angular';
import {DataService} from './data.service';
import {LinkComponent} from "./link.component";
@Component({
selector: 'scheduler-component',
template: `<daypilot-scheduler [config]="config" [events]="events" #scheduler></daypilot-scheduler>`,
styles: [``]
})
export class SchedulerComponent implements AfterViewInit {
constructor(private ds: DataService,
private viewContainerRef: ViewContainerRef) {
}
createLinkComponent(text: string, data?: any): ComponentRef<LinkComponent> {
const component: ComponentRef<LinkComponent> = this.viewContainerRef.createComponent(LinkComponent);
component.instance.text = text;
component.instance.data = data;
component.changeDetectorRef.detectChanges();
return component;
}
// ...
}
The createLinkComponent()
method creates a new instance of the component and sets its properties (text
, data
). It is necessary to apply the changes by calling detectChanges()
method of the change detector.
Don't forget to add the LinkComponent
to entryComponents
section of the SchedulerModule
:
@NgModule({
// ...
entryComponents: [LinkComponent],
// ...
})
export class SchedulerModule { }
As soon as we have the component reference we can add it to the event body using onBeforeEventDomAdd
event handler:
config: DayPilot.Scheduler = {
// ...
onBeforeEventDomAdd: args => {
console.log("Creating LinkComponent for " + args.e.text());
const component = this.createLinkComponent('details', args.e.data);
args.element = component.location.nativeElement;
(<any>args).component = component;
},
}
If you specify args.data.element
in onBeforeEventDomAdd
the specified HTML element will be displayed in the event body instead of the default HTML (event text or html).
There is also an onBeforeEventDomRemove
event handler which is invoked when the Scheduler removes the event from the DOM. We will use it to destroy the component:
config: DayPilot.Scheduler = {
// ...
onBeforeEventDomRemove: args => {
console.log("Destroying LinkComponent for " + args.e.text());
const component = (<any>args).component;
component.destroy();
},
}
The onBeforeEventDomAdd
and onBeforeEventDomRemove
event handlers are supported in DayPilot Pro for JavaScript since build 2019.3.3967.
Prevent Event Bubbling
The Scheduler event element defines its own event handlers. If you handle the click event in the inner component (LinkComponent
) it's necessary to prevent the event from bubbling to the parents using stopPropagation()
call:
import {Component} from '@angular/core';
@Component({
selector: 'simple-component',
template: `<a href="#" (click)="click($event)">{{text}}</a>`
})
export class LinkComponent {
click(e: MouseEvent) {
e.stopPropagation();
// ...
}
}
Performance
Please note that onBeforeEventDomAdd
is called for every event and creating the component takes some time. Rendering complex component inside events may affect performance, especially when there are many events.
The Scheduler renders only the events for the current viewport (see progressive event rendering). This reduces the time needed for initial rendering but the events will be rendered later (during scrolling). Generally, you should try to keep the event structure as simple as possible.
Full Source Code (TypeScript)
This is the source code of the main component with the Angular Scheduler (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 {LinkComponent} from "./link.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: DayPilot.Date.today().daysInMonth(),
startDate: DayPilot.Date.today().firstDayOfMonth(),
treeEnabled: true,
eventDeleteHandling: "Update",
onBeforeEventDomAdd: args => {
console.log("Creating LinkComponent for " + args.e.text());
const component = this.createLinkComponent('details', args.e.data);
args.element = component.location.nativeElement;
(<any>args).component = component;
},
onBeforeEventDomRemove: args => {
console.log("Destroying LinkComponent for " + args.e.text());
const component = (<any>args).component;
component.destroy();
},
onEventClick: args => {
console.log("default event click action");
},
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
});
}
};
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;
});
}
createLinkComponent(text: string, data?: any): ComponentRef<LinkComponent> {
const component: ComponentRef<LinkComponent> = this.viewContainerRef.createComponent(LinkComponent);
component.instance.text = text;
component.instance.data = data;
component.changeDetectorRef.detectChanges();
return component;
}
}
This is the source code of the component that is displayed inside Scheduler events (link.component.ts
):
import {Component} from '@angular/core';
import {DayPilot} from "daypilot-pro-angular";
@Component({
selector: 'link-component',
template: `<a href="#" (click)="click($event)">{{text}}</a>`
})
export class LinkComponent {
public text: string = "link";
public data: any;
click(e: MouseEvent) {
e.preventDefault();
e.stopPropagation();
DayPilot.Modal.alert("Event id: " + this.data.id);
}
}
Source code of the Angular module with the required component registration (scheduler.module.ts
):
import {DataService} from './data.service';
import {FormsModule} from '@angular/forms';
import {BrowserModule} from '@angular/platform-browser';
import {NgModule} from '@angular/core';
import {SchedulerComponent} from './scheduler.component';
import {DayPilotModule} from 'daypilot-pro-angular';
import {HttpClientModule} from '@angular/common/http';
import {LinkComponent} from "./link.component";
@NgModule({
imports: [
BrowserModule,
FormsModule,
HttpClientModule,
DayPilotModule
],
declarations: [
SchedulerComponent,
LinkComponent
],
entryComponents: [LinkComponent],
exports: [ SchedulerComponent ],
providers: [ DataService ]
})
export class SchedulerModule { }