Features
This tutorial shows how to manage resources (displayed as rows) using the built-in UI elements.
Angular Scheduler component that displays a timeline for multiple resources
How to load resources from the server-side backend and display them as rows
How to add a row header context menu (including a hint icon)
How to create resource groups using an inline edit box
How to create child resources using a context menu and modal dialog
How to delete resources using context menu
You can move resources using drag and drop
The Angular project includes a trial version of DayPilot Pro for JavaScript (see License below)
This tutorial focuses on managing resources (rows). For an introduction to the Angular Scheduler component please see the basic tutorial:
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.
How to Load Resources and Display Them as Rows in the Scheduler
The Angular Scheduler component displays resources on the vertical axis.
To specify the resources, use the resources
property of the using the config
object. It’s an array of ResourceData objects:
resources: DayPilot.ResourceData[] = [
{ name: "Group 1", id: "G1", expanded: true, children: [
{ name: "Resource 1", id: "R1"},
{ name: "Resource 2", id: "R2"},
]},
{ name: "Group 2", id: "G2", expanded: true, children: [
{ name: "Resource 3", id: "R3"},
{ name: "Resource 4", id: "R4"},
]},
];
We will use a DataService
class to load the data. The sample DataService
implementation uses locally-generated data for simplicity. In your application, you can replace this logic with HTTP calls that load data using a server-side backend.
The resources are loaded using DataService
in ngAfterViewInit()
method:
this.ds.getResources().subscribe(result => this.config.resources = result);
By default, the Scheduler displays a flat list of resources. We want to use a hierarchy of rows - that needs to be enabled using treeEnabled
property of the config
object:
config: DayPilot.SchedulerConfig = {
treeEnabled: true,
};
A simplified version of SchedulerComponent
that loads resources and events from the data source:
scheduler.component.ts
import {Component, ViewChild, AfterViewInit, ChangeDetectorRef} from "@angular/core";
import {DayPilot, DayPilotSchedulerComponent} from "daypilot-pro-angular";
import {DataService} from "./data.service";
@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: any[] = [];
config: DayPilot.SchedulerConfig = {
timeHeaders : [
{groupBy: "Month", format: "MMMM yyyy"},
{groupBy: "Day", format: "d"}
],
eventHeight: 40,
scale: "Day",
days: 31,
startDate: "2023-01-01",
treeEnabled: true,
// ...
};
constructor(private ds: DataService) {}
ngAfterViewInit(): void {
this.ds.getResources().subscribe(result => this.config.resources = result);
var from = this.scheduler.control.visibleStart();
var to = this.scheduler.control.visibleEnd();
this.ds.getEvents(from, to).subscribe(result => {
this.events = result;
});
}
}
How to Add a Context Menu to the Row Headers
We want to let users access resource-related actions using a context menu. Our context menu will have three items:
Edit
Delete
Add Child
The menu can be created using DayPilot.Menu
object:
menu: DayPilot.Menu = new DayPilot.Menu({
items: [
{text: "Edit..." },
{text: "Delete" },
{text: "-"}, // separator
{text: "Add Child..."}
]
});
Next, we will add an onClick
event handler to each of the items:
menu: DayPilot.Menu = new DayPilot.Menu({
items: [
{
text: "Edit...",
onClick: async args => {
const form = [
{name: "Resource name", id: "name"}
];
const row = args.source;
const data = row.data;
const modal = await DayPilot.Modal.form(form, data);
if (modal.canceled) {
return;
}
const name = modal.result.name;
this.ds.updateResource(row, name).subscribe(result => {
row.data.name = name;
});
}
},
{
text: "Delete",
onClick: args => {
let row = args.source;
this.ds.deleteResource(row).subscribe(result => {
row.remove();
this.scheduler.control.message("Deleted");
});
}
},
{
text: "-"
},
{
text: "Add Child...",
onClick: async args => {
const form = [
{name: "Resource name", id: "name"}
];
const row = args.source;
const data = {};
const modal = await DayPilot.Modal.form(form, data);
if (modal.canceled) {
return;
}
const name = modal.result.name;
this.ds.createResource(row, name).subscribe(resource => {
// add the new resource to the tree
let parent = row.data;
parent.expanded = true;
if (!parent.children) {
parent.children = [];
}
parent.children.push(resource);
});
}
}
]
});
At this moment, we just need to assign the context menu to row headers (resources) using contextMenuResource
property of the config
. The context menu can be activated by right-clicking the row header.
config: DayPilot.SchedulerConfig = {
// ...
contextMenuResource: this.menu
};
This is how our SchedulerComponent
looks like now:
scheduler.component.ts
import {Component, ViewChild, AfterViewInit, ChangeDetectorRef} from "@angular/core";
import {DayPilot, DayPilotSchedulerComponent} from "daypilot-pro-angular";
import {DataService} from "./data.service";
@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: any[] = [];
menu: DayPilot.Menu = new DayPilot.Menu({
items: [
{
text: "Edit...",
onClick: async args => {
const form = [
{name: "Resource name", id: "name"}
];
const row = args.source;
const data = row.data;
const modal = await DayPilot.Modal.form(form, data);
if (modal.canceled) {
return;
}
const name = modal.result.name;
this.ds.updateResource(row, name).subscribe(result => {
row.data.name = name;
});
}
},
{
text: "Delete",
onClick: args => {
let row = args.source;
this.ds.deleteResource(row).subscribe(result => {
row.remove();
this.scheduler.control.message("Deleted");
});
}
},
{
text: "-"
},
{
text: "Add Child...",
onClick: async args => {
const form = [
{name: "Resource name", id: "name"}
];
const row = args.source;
const data = {};
const modal = await DayPilot.Modal.form(form, data);
if (modal.canceled) {
return;
}
const name = modal.result.name;
this.ds.createResource(row, name).subscribe(resource => {
// add the new resource to the tree
let parent = row.data;
parent.expanded = true;
if (!parent.children) {
parent.children = [];
}
parent.children.push(resource);
});
}
}
]
});
config: DayPilot.SchedulerConfig = {
timeHeaders : [
{groupBy: "Month", format: "MMMM yyyy"},
{groupBy: "Day", format: "d"}
],
eventHeight: 40,
scale: "Day",
days: 31,
startDate: "2023-01-01",
treeEnabled: true,
// ...
contextMenuResource: this.menu
};
// ...
}
How to Display a Context Menu Hint Icon
In addition to right-click activation, we also add a hover icon that provides a hint that a context menu is available. We will add an icon in the upper-right corner of the row header. The icon is created using a customizable object called an active area.
The active area uses a chevron SVG icon (daypilot.svg#minichevron-down-2
) from the DayPilot Icons collection (see the assets
directory).
config: DayPilot.SchedulerConfig = {
// ...
onBeforeRowHeaderRender: args => {
args.row.areas = [
{
right: 2,
top: 2,
width: 14,
height: 14,
symbol: "assets/daypilot.svg#minichevron-down-2",
style: "font-size: 12px; background-color: #f9f9f9; border: 1px solid #ccc; padding: 2px; cursor:pointer",
visibility: "Hover",
action: "ContextMenu"
}
];
},
};
scheduler.component.ts
import {Component, ViewChild, AfterViewInit, ChangeDetectorRef} from "@angular/core";
import {DayPilot, DayPilotSchedulerComponent} from "daypilot-pro-angular";
import {DataService} from "./data.service";
@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: any[] = [];
menu: DayPilot.Menu = new DayPilot.Menu({
// ...
});
config: DayPilot.SchedulerConfig = {
timeHeaders : [
{groupBy: "Month", format: "MMMM yyyy"},
{groupBy: "Day", format: "d"}
],
eventHeight: 40,
scale: "Day",
days: 31,
startDate: "2023-01-01",
treeEnabled: true,
// ...
contextMenuResource: this.menu,
onBeforeRowHeaderRender: args => {
args.row.areas = [
{
right: 2,
top: 2,
width: 14,
height: 14,
symbol: "assets/daypilot.svg#minichevron-down-2",
style: "font-size: 12px; background-color: #f9f9f9; border: 1px solid #ccc; padding: 2px; cursor:pointer",
visibility: "Hover",
action: "ContextMenu"
}
];
},
};
// ...
}
How to Create Resource Groups at the Top Level
The Scheduler has a built-in helper for adding new resources at the top level. You can activate it using rowCreateHandling
property of the config
:
config: DayPilot.SchedulerConfig = {
// ...
rowCreateHandling: "Enabled",
};
When in-line row creating is enabled, the Scheduler will display an extra row at the bottom. After clicking the row header the user can enter a name of the new row:
After a user confirms the new name using <enter>
, the Scheduler fires onRowCreate
event handler. We will use this event handler to notify the server-side backend.
scheduler.component.ts
import {Component, ViewChild, AfterViewInit, ChangeDetectorRef} from "@angular/core";
import {DayPilot, DayPilotSchedulerComponent} from "daypilot-pro-angular";
import {DataService} from "./data.service";
@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: any[] = [];
config: DayPilot.SchedulerConfig = {
// ...
rowCreateHandling: "Enabled",
rowCreateHtml: "New group...",
onRowCreate: args => {
// notify the backend; when successful, add the new resource to the config to display it
this.ds.createResource(null, args.text).subscribe(result => {
this.config.resources.push(result);
this.scheduler.control.message("Created.");
});
},
};
}
How to Create Child Resources
The “Add Child…” context menu item lets users add a new child resource.
menu: DayPilot.Menu = new DayPilot.Menu({
items: [
// ...
{
text: "Add Child...",
onClick: async args => {
const form = [
{name: "Resource name", id: "name"}
];
const row = args.source;
const data = {};
const modal = await DayPilot.Modal.form(form, data);
if (modal.canceled) {
return;
}
const name = modal.result.name;
this.ds.createResource(row, name).subscribe(resource => {
// add the new resource to the tree
let parent = row.data;
parent.expanded = true;
if (!parent.children) {
parent.children = [];
}
parent.children.push(resource);
});
}
}
]
});
It uses DayPilot.Modal to display a modal dialog with the specified fields (resource name in this case).
The onClick
event handler of the "Add Child...." menu items opens the modal dialog using DayPilot.Modal.form()
method and passes the row object (available as args.source
) as a data parameter. The args.source
objects holds a DayPilot.Row
object with row details.
How to Delete Resources
In our example, we use a context menu item to delete a resource:
menu: DayPilot.Menu = new DayPilot.Menu({
items: [
// ...
{text: "Delete", onClick: args => {
let row = args.source;
this.ds.deleteResource(row).subscribe(result => {
row.remove();
this.scheduler.control.message("Deleted");
});
} },
// ...
]
});
The onClick
event handler calls the DataService
class. On success, it removes the row from the Scheduler using DayPilot.Row.remove() method.
How to Move Rows using Drag and Drop
The Scheduler supports drag and drop row moving. This feature is disabled by default - you need to enable it using rowMoveHandling
property of the config
:
config: DayPilot.SchedulerConfig = {
// ...
rowMoveHandling: "Update",
// ...
};
When you enable drag and drop row moving the row will display a move handle on hover. Users can drag the row by the handle and move it to a new location. The source row remains highlighted during dragging.
As soon as a user drops the row at the target location onRowMoved
event handler is fired. The event args
object holds the operation details:
args.source
-DayPilot.Row
object with source rowargs.target
- the targetDayPilot.Row
objectargs.position
- drop position relative to target ("child" | "before" | "after"
)
import {Component, ViewChild, AfterViewInit, ChangeDetectorRef} from "@angular/core";
import {DayPilot, DayPilotSchedulerComponent} from "daypilot-pro-angular";
import {DataService} from "./data.service";
@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: any[] = [];
config: DayPilot.SchedulerConfig = {
// ...
rowMoveHandling: "Update",
onRowMoved: args => {
this.ds.moveResource(args.source, args.target, args.position).subscribe(result => this.scheduler.control.message("Moved"));
},
// ...
};
}