DevExtreme v26.1 is now available.

Explore our newest features/capabilities and share your thoughts with us.

Your search did not match any results.

Angular Scheduler - Context Menu

This demo shows how to create a context menu for appointments and cells using the onAppointmentContextMenu and onCellContextMenu functions.

Backend API
<dx-scheduler timeZone="America/Los_Angeles" [crossScrollingEnabled]="crossScrollingEnabled" [dataSource]="appointmentsData" [views]="['day', 'month']" currentView="month" [groups]="groups" recurrenceEditMode="series" [currentDate]="currentDate" (onCellContextMenu)="onCellContextMenu($event)" (onAppointmentContextMenu)="onAppointmentContextMenu($event)" [startDayHour]="9" [height]="730" > <dxi-scheduler-resource fieldExpr="roomId" label="room" [dataSource]="resourcesData" icon="conferenceroomoutline" ></dxi-scheduler-resource> </dx-scheduler> <dx-context-menu [items]="contextMenuItems" target=".dx-scheduler" (onItemClick)="onContextMenuItemClick($event)" (onHiding)="onContextMenuHiding()" [width]="200" > <div *dxTemplate="let e of 'item'"> <div> @if(e.color) { <div class="item-badge" [style.background-color]="e.color"></div> } {{ e.text }} </div> </div> </dx-context-menu>
import { bootstrapApplication } from '@angular/platform-browser'; import { Component, enableProdMode, ViewChild, provideZoneChangeDetection } from '@angular/core'; import { DxSchedulerModule, DxSchedulerComponent, type DxSchedulerTypes, } from 'devextreme-angular/ui/scheduler'; import { DxContextMenuModule, type DxContextMenuTypes } from 'devextreme-angular/ui/context-menu'; import { Service, type Appointment, type Resource, type ContextMenuItem } from './app.service'; if (!/localhost/.test(document.location.host)) { enableProdMode(); } @Component({ selector: 'demo-app', templateUrl: './app.component.html', styleUrls: ['./app.component.css'], providers: [Service], imports: [ DxSchedulerModule, DxContextMenuModule, ], }) export class AppComponent { @ViewChild(DxSchedulerComponent, { static: false }) scheduler!: DxSchedulerComponent; appointmentsData!: Appointment[]; currentDate: Date = new Date(2020, 10, 25); resourcesData: Resource[]; groups: string[] = []; crossScrollingEnabled = false; contextMenuItems: ContextMenuItem[] = []; constructor(service: Service) { this.resourcesData = service.getResources(); this.appointmentsData = service.getAppointments(); } onContextMenuItemClick(e: DxContextMenuTypes.ItemClickEvent<ContextMenuItem>) { e.itemData.onItemClick(e); } onAppointmentContextMenu(e: DxSchedulerTypes.AppointmentContextMenuEvent) { const items = this.getAppointmentContextMenuItems(e); this.contextMenuItems = items; } onCellContextMenu(e: DxSchedulerTypes.CellContextMenuEvent) { const items = this.getCellContextMenuItems(e); this.contextMenuItems = items; } onContextMenuHiding() { this.contextMenuItems = []; } getAppointmentContextMenuItems(e: DxSchedulerTypes.AppointmentContextMenuEvent): ContextMenuItem[] { const { appointmentData: appointment, targetedAppointmentData: targetedAppointment, } = e; const scheduler = this.scheduler.instance; return [ { text: 'Open', onItemClick: () => scheduler.showAppointmentPopup(appointment), }, { text: 'Delete', onItemClick: () => scheduler.deleteAppointment(appointment), }, { text: 'Repeat Weekly', beginGroup: true, onItemClick: () => scheduler.updateAppointment(appointment, { ...appointment, startDate: targetedAppointment.startDate, recurrenceRule: 'FREQ=WEEKLY', }), }, { text: 'Set Room', beginGroup: true, disabled: true, }, ...this.resourcesData.map((item) => ({ ...item, onItemClick: ({ itemData }: DxContextMenuTypes.ItemClickEvent<Resource>) => scheduler.updateAppointment(appointment, { ...appointment, roomId: [itemData.id], }), })), ]; } getCellContextMenuItems(e: DxSchedulerTypes.CellContextMenuEvent): ContextMenuItem[] { const { cellData } = e; const scheduler = this.scheduler.instance; return [ { text: 'New Appointment', onItemClick: () => scheduler.showAppointmentPopup( { startDate: cellData.startDateUTC }, true, ), }, { text: 'New Recurring Appointment', onItemClick: () => scheduler.showAppointmentPopup( { startDate: cellData.startDateUTC, recurrenceRule: 'FREQ=DAILY', }, true, ), }, { text: 'Group by Room/Ungroup', beginGroup: true, onItemClick: () => { if (this.groups.length) { this.crossScrollingEnabled = false; this.groups = []; } else { this.crossScrollingEnabled = true; this.groups = ['roomId']; } }, }, { text: 'Go to Today', onItemClick: () => { this.currentDate = new Date(); }, }, ]; } } bootstrapApplication(AppComponent, { providers: [ provideZoneChangeDetection({ eventCoalescing: true, runCoalescing: true }), ], });
::ng-deep .item-badge { text-align: center; float: left; margin-right: 12px; color: white; width: 18px; height: 18px; font-size: 19.5px; border-radius: 18px; margin-top: 2px; }
import { Injectable } from '@angular/core'; import type { DxContextMenuTypes } from 'devextreme-angular/ui/context-menu'; export interface Appointment { text: string; roomId: number[]; startDate: Date; endDate: Date; recurrenceRule?: string; recurrenceException?: string; } export interface Resource { text: string; id: number; color: string; } export type ContextMenuItem = DxContextMenuTypes.Item & Partial<Resource> & { onItemClick?: (e: DxContextMenuTypes.ItemClickEvent<ContextMenuItem>) => void }; const appointments: Appointment[] = [ { text: 'Watercolor Landscape', roomId: [1], startDate: new Date('2020-11-01T17:30:00.000Z'), endDate: new Date('2020-11-01T19:00:00.000Z'), recurrenceRule: 'FREQ=WEEKLY;BYDAY=MO,TH;COUNT=10', }, { text: 'Oil Painting for Beginners', roomId: [2], startDate: new Date('2020-11-01T17:30:00.000Z'), endDate: new Date('2020-11-01T19:00:00.000Z'), recurrenceRule: 'FREQ=WEEKLY;BYDAY=SU,WE;COUNT=10', }, { text: 'Testing', roomId: [3], startDate: new Date('2020-11-01T20:00:00.000Z'), endDate: new Date('2020-11-01T21:00:00.000Z'), recurrenceRule: 'FREQ=WEEKLY;BYDAY=SU;WKST=TU;INTERVAL=2;COUNT=2', }, { text: 'Meeting of Instructors', roomId: [4], startDate: new Date('2020-11-01T17:00:00.000Z'), endDate: new Date('2020-11-01T17:15:00.000Z'), recurrenceRule: 'FREQ=DAILY;BYDAY=TU;UNTIL=20201203', }, { text: 'Recruiting students', roomId: [5], startDate: new Date('2020-10-24T18:00:00.000Z'), endDate: new Date('2020-10-24T19:00:00.000Z'), recurrenceRule: 'FREQ=YEARLY;BYWEEKNO=50;WKST=SU', recurrenceException: '20201212T190000Z', }, { text: 'Final exams', roomId: [3], startDate: new Date('2020-10-24T20:00:00.000Z'), endDate: new Date('2020-10-24T21:35:00.000Z'), recurrenceRule: 'FREQ=YEARLY;BYWEEKNO=51;BYDAY=WE,TH', }, { text: 'Monthly Planning', roomId: [4], startDate: new Date('2020-11-24T22:30:00.000Z'), endDate: new Date('2020-11-24T23:45:00.000Z'), recurrenceRule: 'FREQ=MONTHLY;BYMONTHDAY=28;COUNT=1', }, { text: 'Open Day', roomId: [5], startDate: new Date('2020-11-01T17:30:00.000Z'), endDate: new Date('2020-11-01T21:00:00.000Z'), recurrenceRule: 'FREQ=YEARLY;BYYEARDAY=333', }, ]; const resources: Resource[] = [ { text: 'Room 101', id: 1, color: '#1564C0', }, { text: 'Room 102', id: 2, color: '#C2185B', }, { text: 'Room 103', id: 3, color: '#7B1FA2', }, { text: 'Meeting room', id: 4, color: '#DD2C00', }, { text: 'Conference hall', id: 5, color: '#2E7D32', }, ]; @Injectable() export class Service { getAppointments(): Appointment[] { return appointments; } getResources(): Resource[] { return resources; } }
<!DOCTYPE html> <html lang="en"> <head> <title>DevExtreme Demo</title> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0" /> <link rel="stylesheet" type="text/css" href="https://cdn3.devexpress.com/jslib/26.1.5/css/dx.light.css" /> </head> <body class="dx-viewport"> <div class="demo-container"> <demo-app>Loading...</demo-app> </div> <script src="https://cdn.jsdelivr.net/npm/zone.js@0.16.2/bundles/zone.umd.js"></script> <script src="./bundle.js" type="module"></script> </body> </html>