How can I help you?
Context Menu in Vue Schedule Component
3 Feb 202624 minutes to read
A context menu can be displayed on work cells and appointments in the Vue Schedule component by manually integrating the ContextMenu ContextMenu control. In this approach, the ContextMenu component is added at the application level and its target property is set to the Schedule component’s DOM element.
On Scheduler cells, menu items such as New Event, New Recurring Event and Today can be displayed. For appointments, context menu options such as Edit Event and Delete Event. The default event editor can be opened for creating and editing appointments using the openEditor method of the Schedule component.
Appointment deletion can be performed using the deleteEvent public method. Date navigation can be achieved using the selectedDate property to programmatically move to the desired date.
Custom menu items can also be displayed for both cells and appointments. In responsive mode, the context menu opens on tap‑hold interaction.
<template>
<div id='app'>
<div id='container'>
<div class='content-wrapper'>
<ejs-schedule id='Schedule' ref='scheduleObj' height='550px' :selectedDate='selectedDate'
:eventSettings='eventSettings'></ejs-schedule>
</div>
<div>
<ejs-contextmenu id='contextmenu' ref='menuObj' cssClass='schedule-context-menu' target='.e-schedule'
:items='menuItems' :beforeOpen='onContextMenuBeforeOpen'
:select='onMenuItemSelect'></ejs-contextmenu>
</div>
</div>
</div>
</template>
<script setup>
import { provide, ref } from "vue";
import { scheduleData } from './datasource.js';
import { extend, closest, isNullOrUndefined, remove, removeClass } from '@syncfusion/ej2-base';
import { DataManager, Query } from '@syncfusion/ej2-data';
import { ScheduleComponent as EjsSchedule, Day, Week, WorkWeek, Month, Agenda } from '@syncfusion/ej2-vue-schedule';
import { ContextMenuComponent as EjsContextmenu } from '@syncfusion/ej2-vue-navigations';
const scheduleObj = ref(null);
const menuObj = ref(null);
const eventSettings = { dataSource: extend([], scheduleData, null, true) };
const selectedDate = new Date(2018, 1, 15);
const menuItems = [
{
text: 'New Event',
iconCss: 'e-icons new',
id: 'Add'
}, {
text: 'New Recurring Event',
iconCss: 'e-icons recurrence',
id: 'AddRecurrence'
}, {
text: 'Today',
iconCss: 'e-icons today',
id: 'Today'
}, {
text: 'Edit Event',
iconCss: 'e-icons edit',
id: 'Save'
}, {
text: 'Edit Event',
id: 'EditRecurrenceEvent',
iconCss: 'e-icons edit',
items: [{
text: 'Edit Occurrence',
id: 'EditOccurrence'
}, {
text: 'Edit Series',
id: 'EditSeries'
}]
}, {
text: 'Delete Event',
iconCss: 'e-icons delete',
id: 'Delete'
}, {
text: 'Delete Event',
id: 'DeleteRecurrenceEvent',
iconCss: 'e-icons delete',
items: [{
text: 'Delete Occurrence',
id: 'DeleteOccurrence'
}, {
text: 'Delete Series',
id: 'DeleteSeries'
}]
}
];
const selectedTarget = null;
const onContextMenuBeforeOpen = function (args) {
let schedule = scheduleObj.value;
let newEventElement = schedule.querySelector('.e-new-event');
if (newEventElement) {
remove(newEventElement);
removeClass([schedule.querySelector('.e-selected-cell')], 'e-selected-cell');
}
schedule.closeQuickInfoPopup();
let targetElement = args.event.target;
if (closest(targetElement, '.e-contextmenu')) {
return;
}
let menu = menuObj.value;
selectedTarget = closest(targetElement, '.e-appointment,.e-work-cells,' +
'.e-vertical-view .e-date-header-wrap .e-all-day-cells,.e-vertical-view .e-date-header-wrap .e-header-cells');
if (isNullOrUndefined(selectedTarget)) {
args.cancel = true;
return;
}
if (selectedTarget.classList.contains('e-appointment')) {
let eventObj = schedule.getEventDetails(selectedTarget);
if (eventObj.RecurrenceRule) {
menu.showItems(['EditRecurrenceEvent', 'DeleteRecurrenceEvent'], true);
menu.hideItems(['Add', 'AddRecurrence', 'Today', 'Save', 'Delete'], true);
} else {
menu.showItems(['Save', 'Delete'], true);
menu.hideItems(['Add', 'AddRecurrence', 'Today', 'EditRecurrenceEvent', 'DeleteRecurrenceEvent'], true);
}
return;
}
menu.hideItems(['Save', 'Delete', 'EditRecurrenceEvent', 'DeleteRecurrenceEvent'], true);
menu.showItems(['Add', 'AddRecurrence', 'Today'], true);
}
const onMenuItemSelect = function (args) {
let schedule = scheduleObj.value.ej2Instances.ej2Instances;
let selectedMenuItem = args.item.id;
let eventObj;
if (selectedTarget.classList.contains('e-appointment')) {
eventObj = schedule.getEventDetails(selectedTarget);
}
switch (selectedMenuItem) {
case 'Today':
schedule.selectedDate = new Date();
break;
case 'Add':
case 'AddRecurrence':
let selectedCells = schedule.getSelectedElements();
let activeCellsData = schedule.getCellDetails(selectedCells.length > 0 ? selectedCells : selectedTarget);
if (selectedMenuItem === 'Add') {
schedule.openEditor(activeCellsData, 'Add');
} else {
schedule.openEditor(activeCellsData, 'Add', null, 1);
}
break;
case 'Save':
case 'EditOccurrence':
case 'EditSeries':
if (selectedMenuItem === 'EditSeries') {
eventObj = new DataManager(schedule.eventsData).
executeLocal(new Query().where(schedule.eventFields.id, 'equal', eventObj[schedule.eventFields.recurrenceID]))[0];
}
schedule.openEditor(eventObj, selectedMenuItem);
break;
case 'Delete':
schedule.deleteEvent(eventObj);
break;
case 'DeleteOccurrence':
case 'DeleteSeries':
schedule.deleteEvent(eventObj, selectedMenuItem);
break;
}
}
provide('schedule', [Day, Week, WorkWeek, Month, Agenda]);
</script>
<style>
@import "../node_modules/@syncfusion/ej2-base/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-buttons/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-calendars/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-dropdowns/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-inputs/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-navigations/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-popups/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-schedule/styles/material3.css";
.schedule-context-menu .e-menu-item .new::before {
content: '\e7f9';
}
.schedule-context-menu .e-menu-item .edit::before {
content: '\ea9a';
}
.schedule-context-menu .e-menu-item .recurrence::before {
content: '\e308';
font-weight: bold;
}
.schedule-context-menu .e-menu-item .today::before {
content: '\e322';
}
.schedule-context-menu .e-menu-item .delete::before {
content: '\e94a';
}
.e-bigger .schedule-context-menu ul .e-menu-item .e-menu-icon {
font-size: 14px;
}
.schedule-context-menu ul .e-menu-item .e-menu-icon {
font-size: 12px;
}
.schedule-context-menu .e-menu-parent.e-ul .e-menu-item {
font-family: "Roboto", "Segoe UI", "GeezaPro", "DejaVu Serif", "sans-serif", "-apple-system", "BlinkMacSystemFont";
}
</style><template>
<div id='app'>
<div id='container'>
<div class='content-wrapper'>
<ejs-schedule id='Schedule' ref='scheduleObj' height='550px' :selectedDate='selectedDate'
:eventSettings='eventSettings'></ejs-schedule>
</div>
<div>
<ejs-contextmenu id='contextmenu' ref='menuObj' cssClass='schedule-context-menu' target='.e-schedule'
:items='menuItems' :beforeOpen='onContextMenuBeforeOpen'
:select='onMenuItemSelect'></ejs-contextmenu>
</div>
</div>
</div>
</template>
<script>
import { scheduleData } from './datasource.js';
import { extend, closest, isNullOrUndefined, remove, removeClass } from '@syncfusion/ej2-base';
import { DataManager, Query } from '@syncfusion/ej2-data';
import { ScheduleComponent, Day, Week, WorkWeek, Month, Agenda } from '@syncfusion/ej2-vue-schedule';
import { ContextMenuComponent } from '@syncfusion/ej2-vue-navigations';
export default {
name: "App",
components: {
"ejs-schedule": ScheduleComponent,
"ejs-contextmenu": ContextMenuComponent
},
data: function () {
return {
eventSettings: { dataSource: extend([], scheduleData, null, true) },
selectedDate: new Date(2018, 1, 15),
menuItems: [
{
text: 'New Event',
iconCss: 'e-icons new',
id: 'Add'
}, {
text: 'New Recurring Event',
iconCss: 'e-icons recurrence',
id: 'AddRecurrence'
}, {
text: 'Today',
iconCss: 'e-icons today',
id: 'Today'
}, {
text: 'Edit Event',
iconCss: 'e-icons edit',
id: 'Save'
}, {
text: 'Edit Event',
id: 'EditRecurrenceEvent',
iconCss: 'e-icons edit',
items: [{
text: 'Edit Occurrence',
id: 'EditOccurrence'
}, {
text: 'Edit Series',
id: 'EditSeries'
}]
}, {
text: 'Delete Event',
iconCss: 'e-icons delete',
id: 'Delete'
}, {
text: 'Delete Event',
id: 'DeleteRecurrenceEvent',
iconCss: 'e-icons delete',
items: [{
text: 'Delete Occurrence',
id: 'DeleteOccurrence'
}, {
text: 'Delete Series',
id: 'DeleteSeries'
}]
}
],
selectedTarget: null
}
},
provide: {
schedule: [Day, Week, WorkWeek, Month, Agenda]
},
methods: {
onContextMenuBeforeOpen: function (args) {
let scheduleObj = this.$refs.scheduleObj;
let newEventElement = scheduleObj.querySelector('.e-new-event');
if (newEventElement) {
remove(newEventElement);
removeClass([scheduleObj.querySelector('.e-selected-cell')], 'e-selected-cell');
}
scheduleObj.closeQuickInfoPopup();
let targetElement = args.event.target;
if (closest(targetElement, '.e-contextmenu')) {
return;
}
let menuObj = this.$refs.menuObj;
this.selectedTarget = closest(targetElement, '.e-appointment,.e-work-cells,' +
'.e-vertical-view .e-date-header-wrap .e-all-day-cells,.e-vertical-view .e-date-header-wrap .e-header-cells');
if (isNullOrUndefined(this.selectedTarget)) {
args.cancel = true;
return;
}
if (this.selectedTarget.classList.contains('e-appointment')) {
let eventObj = scheduleObj.getEventDetails(this.selectedTarget);
if (eventObj.RecurrenceRule) {
menuObj.showItems(['EditRecurrenceEvent', 'DeleteRecurrenceEvent'], true);
menuObj.hideItems(['Add', 'AddRecurrence', 'Today', 'Save', 'Delete'], true);
} else {
menuObj.showItems(['Save', 'Delete'], true);
menuObj.hideItems(['Add', 'AddRecurrence', 'Today', 'EditRecurrenceEvent', 'DeleteRecurrenceEvent'], true);
}
return;
}
menuObj.hideItems(['Save', 'Delete', 'EditRecurrenceEvent', 'DeleteRecurrenceEvent'], true);
menuObj.showItems(['Add', 'AddRecurrence', 'Today'], true);
},
onMenuItemSelect: function (args) {
let scheduleObj = this.$refs.scheduleObj.ej2Instances;
let selectedMenuItem = args.item.id;
let eventObj;
if (this.selectedTarget.classList.contains('e-appointment')) {
eventObj = scheduleObj.getEventDetails(this.selectedTarget);
}
switch (selectedMenuItem) {
case 'Today':
scheduleObj.selectedDate = new Date();
break;
case 'Add':
case 'AddRecurrence':
let selectedCells = scheduleObj.getSelectedElements();
let activeCellsData = scheduleObj.getCellDetails(selectedCells.length > 0 ? selectedCells : this.selectedTarget);
if (selectedMenuItem === 'Add') {
scheduleObj.openEditor(activeCellsData, 'Add');
} else {
scheduleObj.openEditor(activeCellsData, 'Add', null, 1);
}
break;
case 'Save':
case 'EditOccurrence':
case 'EditSeries':
if (selectedMenuItem === 'EditSeries') {
eventObj = new DataManager(scheduleObj.eventsData).
executeLocal(new Query().where(scheduleObj.eventFields.id, 'equal', eventObj[scheduleObj.eventFields.recurrenceID]))[0];
}
scheduleObj.openEditor(eventObj, selectedMenuItem);
break;
case 'Delete':
scheduleObj.deleteEvent(eventObj);
break;
case 'DeleteOccurrence':
case 'DeleteSeries':
scheduleObj.deleteEvent(eventObj, selectedMenuItem);
break;
}
}
}
}
</script>
<style>
@import "../node_modules/@syncfusion/ej2-base/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-buttons/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-calendars/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-dropdowns/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-inputs/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-navigations/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-popups/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-schedule/styles/material3.css";
.schedule-context-menu .e-menu-item .new::before {
content: '\e7f9';
}
.schedule-context-menu .e-menu-item .edit::before {
content: '\ea9a';
}
.schedule-context-menu .e-menu-item .recurrence::before {
content: '\e308';
font-weight: bold;
}
.schedule-context-menu .e-menu-item .today::before {
content: '\e322';
}
.schedule-context-menu .e-menu-item .delete::before {
content: '\e94a';
}
.e-bigger .schedule-context-menu ul .e-menu-item .e-menu-icon {
font-size: 14px;
}
.schedule-context-menu ul .e-menu-item .e-menu-icon {
font-size: 12px;
}
.schedule-context-menu .e-menu-parent.e-ul .e-menu-item {
font-family: "Roboto", "Segoe UI", "GeezaPro", "DejaVu Serif", "sans-serif", "-apple-system", "BlinkMacSystemFont";
}
</style>Refer to the Vue Scheduler Vue Scheduler feature tour page for detailed feature descriptions. Explore the Vue Scheduler example examples for demonstrations of scheduling and interaction behaviors.