Reactive Forms
The Scheduler provides options for editing its events by using Reactive Angular Forms.
Basic Concepts
To modify a Scheduler event or add a new one:
- Call the
addEvent
or theeditEvent
method respectively. - Pass the
FormGroup
property as one of its parameters. TheFormGroup
configuration is part of the Angular Forms package.
Setup
To enable the editing mode of the Scheduler in Angular Reactive Forms, attach handlers for the corresponding CRUD data operations.
-
When the user click or double-clicks an empty slot in the Scheduler, the component emits the
slotClick
andslotDblClick
events. To initiate thecreate
action, utilize either of those events.ts<kendo-scheduler [kendoSchedulerBinding]="events" (slotDblClick)="onSlotDblClick($event)" > <!-- the rest of the configuration --> </kendo-scheduler>
-
When the user clicks or double-clicks an event in the Scheduler, the component emits the
eventClick
andeventDblClick
events. To start editing the event in the Scheduler, utilize either of those events and then initiate theupdate
action.ts<kendo-scheduler [kendoSchedulerBinding]="events" (slotDblClick)="onSlotDblClick($event)" (eventDblClick)="onEventDblClick($event)" > <!-- the rest of the configuration --> </kendo-scheduler>
-
Attach handlers for the
save
,cancel
, andremove
events.ts<kendo-scheduler [kendoSchedulerBinding]="events" (slotDblClick)="onSlotDblClick($event)" (eventDblClick)="onEventDblClick($event)" (cancel)="cancelHandler($event)" (save)="saveHandler($event)" (remove)="removeHandler($event)" > <!-- the rest of the configuration --> </kendo-scheduler>
Edit Operations
The Scheduler provides the following specific edit operations for its displayed events:
- Editing existing events
- Adding events
- Saving events
- Removing events
- Cancelling the editing of an event
- Dragging events
- Resizing events
Editing Existing Events
The FormGroup
configuration describes the view model for that editor.
The EditMode
contains information about the current editing operation. The supported values are:
Series
—Used when the user edits a recurring event instance and the update applies to all events in the series.Occurrence
—Used when the user edits a recurring event instance and the update applies to this instance only.Event
—Used by default when the user edits a non-recurring event.
To enable the edit mode of an event in the Scheduler, use either of the following approaches:
-
Inside the corresponding event handler (for example,
eventDblClick
), pass the event to theeditEvent
method and provide the appropriate editing options as demonstrated in the following example.tspublic eventDblClickHandler({ sender, event }): void { this.closeEditor(sender); // Define all event fields, validators, and default values. this.formGroup = this.formBuilder.group({ 'TaskID': event.id, 'Start': [event.start, Validators.required], 'End': [event.end, Validators.required], 'StartTimezone': [event.startTimezone], 'EndTimezone': [event.endTimezone], 'IsAllDay': event.isAllDay, 'Title': event.title, 'Description': event.description, 'RecurrenceRule': event.recurrenceRule, 'RecurrenceID': event.recurrenceId }); sender.editEvent(event, { group: this.formGroup, mode: <Edit Mode> }); }
-
Enable the edit mode of the Scheduler by utilizing its
openRecurringConfirmationDialog
method.tspublic eventDblClickHandler({ sender, event }: SlotClickEvent): void { this.closeEditor(sender); let dataItem = event.dataItem; if (this.editService.isRecurring(dataItem)) { sender.openRecurringConfirmationDialog(CrudOperation.Edit) // The result will be undefined if the dialog was closed. .pipe(filter(editMode => editMode !== undefined)) .subscribe((editMode: EditMode) => { if (editMode === EditMode.Series) { dataItem = this.editService.findRecurrenceMaster(dataItem); } this.formGroup = this.formBuilder.group({ <!-- FormGroup Confguration --> }); sender.editEvent(dataItem, { group: this.formGroup, mode: editMode }); }); } else { this.formGroup = this.formBuilder.group({ <!-- FormGroup Confguration --> }); sender.editEvent(dataItem, { group: this.formGroup }); } }
Adding New Events
To show the edit dialog for the new event, call the addEvent
method inside the corresponding event handler (for example, slotDblClick
) and provide the FormGroup
configuration which describes the view model. You will receive the corresponding information for the clicked slot as an event argument—for example, start
, end
, or isAllDay
.
public slotDblClickHandler({ sender, start, end, isAllDay }: EventClickEvent): void {
this.closeEditor(sender);
this.formGroup = this.formBuilder.group({
'Start': [start, Validators.required],
'End': [end, Validators.required],
'StartTimezone': new FormControl(),
'EndTimezone': new FormControl(),
'IsAllDay': isAllDay,
'Title': new FormControl(''),
'Description': new FormControl(''),
'RecurrenceRule': new FormControl(),
'RecurrenceID': new FormControl()
});
sender.addEvent(this.formGroup);
}
Saving Events
When the user clicks the Save action of the edit dialog, the Scheduler fires its save
event.
When an event is saved (updated), the Scheduler provides the following possible options:
-
Updating the event depending on the currently selected edit mode.
-
Switching the current event back to its view mode through its
closeEvent
method.tspublic saveHandler({ sender, formGroup, isNew, dataItem, mode }: SaveEvent): void { if (formGroup.valid) { const formValue = formGroup.value; if (isNew) { this.editService.create(formValue); } else { this.handleUpdate(dataItem, formValue, mode); } this.closeEditor(sender); } }
The saving of events can result in the following actions:
- An update of a non-recurring item.
- An update of a recurring item along with all items in the series.
- An update of a single instance of recurring items, that is, creating an exception.
- An update of an already existing exception.
private handleUpdate(item: any, value: any, mode: EditMode): void {
const service = this.editService;
if (mode === EditMode.Occurrence) {
if (service.isException(item)) {
service.update(item, value);
} else {
service.createException(item, value);
}
} else {
// The item is non-recurring or you are editing the entire series.
service.update(item, value);
}
}
Removing Existing Events
When the user clicks the Remove icon of a displayed event, the Scheduler fires its remove
event.
Enable the edit mode of the Scheduler during the removal of the event by utilizing its openRecurringConfirmationDialog
or openRemoveConfirmationDialog
method inside the event handler.
public removeHandler({ sender, dataItem }: RemoveEvent): void {
if (this.editService.isRecurring(dataItem)) {
sender.openRecurringConfirmationDialog(CrudOperation.Remove)
// The result will be undefined if the dialog was closed.
.pipe(filter(editMode => editMode !== undefined))
.subscribe((editMode) => {
this.handleRemove(dataItem, editMode);
});
} else {
sender.openRemoveConfirmationDialog().subscribe((shouldRemove) => {
if (shouldRemove) {
this.editService.remove(dataItem);
}
});
}
}
The removal of events can result in the following operations:
- A removal of a non-recurring item.
- A removal of a recurring item along with all items in the series.
- A removal of a single instance of recurring items, that is, creating an exception.
- A removal of an already existing exception.
private handleRemove(item: any, mode: EditMode): void {
const service = this.editService;
if (mode === EditMode.Series) {
service.removeSeries(item);
} else if (mode === EditMode.Occurrence) {
if (service.isException(item)) {
service.remove(item);
} else {
service.removeOccurrence(item);
}
} else {
service.remove(item);
}
}
Cancelling Event Editing
When the user clicks the Cancel action of the edit dialog, the Scheduler fires the cancel
event. Inside the event handler, switch the event back to view mode by calling the closeEvent
method.
public cancelHandler({ sender }): void {
scheduler.closeEvent();
this.formGroup = undefined;
}
Dragging Events
The Scheduler provides options for editing its events by dragging them to another date or time slot. To update the events after they are dragged by the user, handle the dragEnd
event of the Scheduler. The event argument contains the start and end date, the isAllDay
value, and the resources as updated after dragging.
To disable the dragging of events, use the editable
and drag
options.
public dragEndHandler({ sender, event, start, end, isAllDay }): void {
const value = { Start: start, End: end, IsAllDay: isAllDay };
let dataItem = event.dataItem;
if (this.editService.isRecurring(dataItem)) {
sender.openRecurringConfirmationDialog(CrudOperation.Edit)
.pipe(filter(editMode => editMode !== undefined))
.subscribe((editMode: EditMode) => {
if (editMode === EditMode.Series) {
dataItem = this.editService.findRecurrenceMaster(dataItem);
// Apply only the changes that were made to the occurrence during dragging to avoid overriding the recurrence head date.
value.Start = this.seriesDate(dataItem.Start, event.dataItem.Start, start);
value.End = this.seriesDate(dataItem.End, event.dataItem.End, end);
} else {
// Create an exception.
value = { ...dataItem, ...value };
}
this.handleUpdate(dataItem, value, editMode);
});
} else {
this.handleUpdate(dataItem, value);
}
}
private seriesDate(head: Date, occurence: Date, current: Date): Date {
const year = occurence.getFullYear() === current.getFullYear() ? head.getFullYear() : current.getFullYear();
const month = occurence.getMonth() === current.getMonth() ? head.getMonth() : current.getMonth();
const date = occurence.getDate() === current.getDate() ? head.getDate() : current.getDate();
const hours = occurence.getHours() === current.getHours() ? head.getHours() : current.getHours();
const minutes = occurence.getMinutes() === current.getMinutes() ? head.getMinutes() : current.getMinutes();
return new Date(year, month, date, hours, minutes);
}
Resizing Events
The Scheduler provides options for changing the start and end date of an event by dragging its resize handles. To update the events after they are resized by the user, handle the resizeEnd
event of the Scheduler. The event argument contains the start and end date as updated after resizing.
To disable the resizing of events, use the editable
and resize
options.
public resizeEndHandler({ sender, event, start, end }): void {
const value = { Start: start, End: end };
let dataItem = event.dataItem;
if (this.editService.isRecurring(dataItem)) {
sender.openRecurringConfirmationDialog(CrudOperation.Edit)
.pipe(filter(editMode => editMode !== undefined))
.subscribe((editMode: EditMode) => {
if (editMode === EditMode.Series) {
dataItem = this.editService.findRecurrenceMaster(dataItem);
// Apply only the changes that were made to the occurrence during resizing to avoid overriding the recurrence head date.
value.Start = this.seriesDate(dataItem.Start, event.dataItem.Start, start);
value.End = this.seriesDate(dataItem.End, event.dataItem.End, end);
} else {
// Create an exception.
value = { ...dataItem, ...value };
}
this.handleUpdate(dataItem, value, editMode);
});
} else {
this.handleUpdate(dataItem, value);
}
}
private seriesDate(head: Date, occurence: Date, current: Date): Date {
const year = occurence.getFullYear() === current.getFullYear() ? head.getFullYear() : current.getFullYear();
const month = occurence.getMonth() === current.getMonth() ? head.getMonth() : current.getMonth();
const date = occurence.getDate() === current.getDate() ? head.getDate() : current.getDate();
const hours = occurence.getHours() === current.getHours() ? head.getHours() : current.getHours();
const minutes = occurence.getMinutes() === current.getMinutes() ? head.getMinutes() : current.getMinutes();
return new Date(year, month, date, hours, minutes);
}