
| Demo | |
|---|---|
| Downloads | |
| Languages | TypeScript |
| Technologies | angular |
Features
- Render a real Angular child component inside Angular Scheduler events using an event template.
- Pass the typed event context to the child and use Angular bindings and click handlers.
- Let the Angular wrapper manage template views as events are rendered and removed.
- Includes 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.
Installation and Versions
The downloadable project uses standalone Angular 22 components and DayPilot Pro Angular 2026.4.7108. This is a sandbox development build, not a stable release. The event-template exports and strict Angular template compilation were tested with this exact package.
To start a new project, open DayPilot UI Builder, select Scheduler, Angular and Pro, and download the generated project. Replace its DayPilot dependency with the sandbox package listed at the DayPilot npm repository. For the version used here, run npm install https://npm.daypilot.org/daypilot-pro-angular/trial/2026.4.7108.tar.gz. Keep the resulting package.json and package-lock.json together; npm’s latest tag does not select this sandbox build.
For the included project, run npm ci, then npm start and open the local URL printed by Angular. Use npm run build for a production build. The project enables strictTemplates.
Angular Scheduler Event Customization
The Angular Scheduler component offers several tools to customize event content. By default, an event displays the text property from its event data object. The html property supplies HTML instead. You can also customize event HTML in onBeforeEventRender, or add positioned interactive elements using active areas.
For Angular content, define an event template inside <daypilot-scheduler>. It supplies the event body as an Angular view, so it can contain a child component with inputs, bindings and event handlers. An HTML string alone does not create that Angular component.
Rendering a Child Component with an Event Template
The containing standalone component imports DayPilotModule from daypilot-pro-angular and the child EventContentComponent. The module exposes the Scheduler and template directive. Put the template inside the Scheduler element:
<daypilot-scheduler [config]="config" [events]="events">
<ng-template daypilotTemplate="event"
daypilotTemplateFor="scheduler"
let-event="event">
<app-event-content [event]="event"></app-event-content>
</ng-template>
</daypilot-scheduler>
daypilotTemplate="event" selects the event body. daypilotTemplateFor="scheduler" identifies the Scheduler context for template type checking. Use the named binding let-event="event": the context contains an event property, not an implicit value. That property is a DayPilot.Event, which the [event] binding passes to the child input.
Create event-content.component.ts as a standalone child component. Its required input is typed as DayPilot.Event. Because this is a signal input, call event() to read it; event().text() returns the current event title. Both the visible title and the button’s accessible label use Angular bindings.
import {Component, input} from '@angular/core';
import {DayPilot} from 'daypilot-pro-angular';
@Component({
selector: 'app-event-content',
standalone: true,
template: `
<span class="event-title">{{ event().text() }}</span>
<button type="button" (click)="showDetails($event)"
[attr.aria-label]="'Details for ' + event().text()">details</button>
`,
styles: [`
:host { display: flex; align-items: center; gap: 8px; height: 100%; }
.event-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
button { font: inherit; color: #0645ad; text-decoration: underline;
background: none; border: 0; padding: 0; cursor: pointer; flex-shrink: 0; }
`]
})
export class EventContentComponent {
event = input.required<DayPilot.Event>();
showDetails(click: MouseEvent): void {
click.stopPropagation();
const event = this.event();
DayPilot.Modal.alert('Event id: ' + event.id());
}
}
The sample uses a button styled as a details link. Clicking it opens a DayPilot modal showing the event ID. Clicking the title or event background instead opens the Scheduler’s event-name prompt. Rename an event to see the Angular title and accessible label update together.
Prevent Event Bubbling
The Scheduler event element defines its own click handler. The child’s showDetails() handler calls stopPropagation() before opening the details dialog so this click does not also open the parent’s rename prompt. The button uses type="button", so it does not submit a surrounding form. If you use an anchor with an href instead, also prevent its default navigation.
View Lifecycle and Application State
The Angular wrapper creates a template view when the Scheduler needs an event’s DOM content and destroys that view when the content is removed. It also disposes remaining template views when the Scheduler component is destroyed. The child’s normal Angular lifecycle follows that view; application code does not need to keep component references or call the change detector manually.
Treat each rendered child as a temporary view of event data. Scrolling, event updates and redraws can remove and recreate it. State that must survive those operations belongs in application data, keyed by event ID, rather than only in a child field. Persist it to your backend as well if it must survive a page reload.
Here, an events signal owns the event data. Creating, renaming, moving, resizing or deleting an event replaces that signal value. For move, resize and delete, the handlers prevent the default control-side update and update application data instead. The wrapper then receives the new signal value. The static options remain in a typed DayPilot.SchedulerConfig object; if options need to change later, replace the configuration or use a configuration signal instead of relying on nested object mutation.
Performance
Complex Angular child components still take time to create, check and destroy. Event templates manage integration and cleanup; they do not automatically improve performance. Keep the event structure as simple as possible, particularly when displaying many events.
The Scheduler uses progressive event rendering to render events for the visible viewport. This reduces initial rendering work, but more content may be rendered during scrolling. Avoid expensive per-event work and keep subscriptions or other resources scoped to the child’s lifecycle.
Compatibility: Manual DOM Callbacks
For advanced manual integration, see onBeforeEventDomAdd and onBeforeEventDomRemove. A template overrides the corresponding DOM-add callback, but an existing DOM-removal callback still runs. When migrating a manually created child component, remove both old handlers, including the stored-component destruction code. The template wrapper owns cleanup in this example.
Full Source Code (TypeScript)
The main component, scheduler.component.ts, imports the child directly. Resources and current-month sample events come from DataService. Drag an empty time range to create an event, drag an event to move it, or drag its edge to resize it. The event delete affordance removes it from application data.
import {Component, inject, signal} from '@angular/core';
import {DayPilot, DayPilotModule} from 'daypilot-pro-angular';
import {DataService} from './data.service';
import {EventContentComponent} from './event-content.component';
@Component({
selector: 'scheduler-component',
standalone: true,
imports: [DayPilotModule, EventContentComponent],
providers: [DataService],
template: `
<daypilot-scheduler [config]="config" [events]="events">
<ng-template daypilotTemplate="event"
daypilotTemplateFor="scheduler"
let-event="event">
<app-event-content [event]="event"></app-event-content>
</ng-template>
</daypilot-scheduler>
`
})
export class SchedulerComponent {
private ds = inject(DataService);
private start = DayPilot.Date.today().firstDayOfMonth();
events = signal<DayPilot.EventData[]>(this.ds.getEvents(this.start));
config: DayPilot.SchedulerConfig = {
startDate: this.start,
days: this.start.daysInMonth(),
scale: 'Day',
timeHeaders: [{groupBy: 'Month'}, {groupBy: 'Day', format: 'd'}],
cellWidth: 40,
treeEnabled: true,
resources: this.ds.resources,
eventHeight: 36,
eventClickHandling: 'Enabled',
eventMoveHandling: 'Update',
eventResizeHandling: 'Update',
eventDeleteHandling: 'Update',
onEventClick: async args => {
const modal = await DayPilot.Modal.prompt('Event name:', args.e.text());
if (modal.canceled || !modal.result) { return; }
this.events.update(events => events.map(event =>
event.id === args.e.id() ? {...event, text: modal.result} : event));
},
onEventMove: args => {
args.preventDefault();
this.events.update(events => events.map(event => event.id === args.e.id()
? {...event, start: args.newStart, end: args.newEnd, resource: args.newResource}
: event));
},
onEventResize: args => {
args.preventDefault();
this.events.update(events => events.map(event => event.id === args.e.id()
? {...event, start: args.newStart, end: args.newEnd} : event));
},
onEventDelete: args => {
args.preventDefault();
this.events.update(events => events.filter(event => event.id !== args.e.id()));
},
onTimeRangeSelected: async args => {
const modal = await DayPilot.Modal.prompt('Create a new event:', 'Event 4');
args.control.clearSelection();
if (modal.canceled || !modal.result) { return; }
this.events.update(events => [...events, {
id: DayPilot.guid(), start: args.start, end: args.end,
resource: args.resource, text: modal.result
}]);
}
};
}
The data service (data.service.ts) supplies representative grouped resources and events:
import {Injectable} from '@angular/core';
import {DayPilot} from 'daypilot-pro-angular';
@Injectable()
export class DataService {
resources: DayPilot.ResourceData[] = [
{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'},
{name: 'Resource 4', id: 'R4'}
]}
];
getEvents(start: DayPilot.Date): DayPilot.EventData[] {
return [
{id: '1', resource: 'R1', start: start.addDays(1), end: start.addDays(7), text: 'Event 1'},
{id: '2', resource: 'R2', start: start.addDays(2), end: start.addDays(8), text: 'Event 2'},
{id: '3', resource: 'R3', start: start.addDays(5), end: start.addDays(11), text: 'Event 3'}
];
}
}
DayPilot




