
Syncfusion Angular Context Menu
- 152 installs
- Updated August 4, 2026
- syncfusion/angular-ui-components-skills
Use syncfusion-angular-context-menu for development tasks
About
syncfusion-angular-context-menu: A skill for development. This provides functionality for development workflows.
- syncfusion-angular-context-menu
Syncfusion Angular Context Menu by the numbers
- 152 all-time installs (skills.sh)
- +5 installs in the week ending Jul 27, 2026 (Skillselion tracking)
- Ranked #2,453 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/syncfusion/angular-ui-components-skills --skill syncfusion-angular-context-menuAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 152 |
|---|---|
| Last updated | August 4, 2026 |
| Repository | syncfusion/angular-ui-components-skills ↗ |
What it does
Use syncfusion-angular-context-menu for development tasks
Files
Implementing Syncfusion Angular ContextMenu
The ContextMenu is a graphical user interface that appears when users right-click or perform touch-hold actions. It provides a context-aware menu with support for nested items, dynamic updates, animations, custom templates, and comprehensive event handling. This skill guides you through implementing, configuring, and customizing context menus for Angular applications.
When to Use This Skill
Use this skill when:
- You need to create a right-click or touch-hold context menu
- Managing menu items dynamically (add, remove, enable, disable)
- Handling menu item click events and actions
- Opening dialogs or navigating from menu selections
- Customizing menu appearance with animations, icons, or themes
- Showing/hiding items based on context or user permissions
- Binding menu items from data sources
- Creating complex menu templates or nested structures
- Implementing responsive menus with scrolling
- Adding keyboard shortcuts or accessibility features
Component Overview
The ContextMenu component enables intuitive right-click interfaces with:
- ✅ Dynamic item management (add/remove/enable/disable)
- ✅ Multi-level nested menus
- ✅ Data binding from arrays or objects
- ✅ Customizable animations (FadeIn, SlideDown, ZoomIn, None)
- ✅ Template support for rich content (icons, HTML, tables)
- ✅ Event handling (click, open, close)
- ✅ Icon and URL navigation
- ✅ Scrollable menus for large item lists
- ✅ Accessibility with keyboard support
Documentation and Navigation Guide
Getting Started
📄 Read: references/getting-started.md
- Package installation and dependencies
- Angular environment setup (standalone architecture)
- Creating your first ContextMenu
- Configuring target elements
- Basic menu item structure
Menu Items Management
📄 Read: references/menu-items-management.md
- Adding menu items dynamically (insertBefore, insertAfter)
- Removing menu items (removeItems method)
- Enabling and disabling items (enableItems)
- Showing and hiding items (showItems, hideItems)
- Dynamic context-aware menus
- Multi-level nested menus
Data Binding
📄 Read: references/data-binding.md
- Populating items from data sources
- MenuItemModel structure and properties
- Parent-child item relationships
- beforeItemRender event for item formatting
- Dynamic data updates
Interaction & Events
📄 Read: references/interaction-and-events.md
- Menu item click handlers (select event)
- Click-to-open submenus (showItemOnClick)
- Programmatic open and close methods
- Menu positioning with coordinates
- Opening dialogs on item selection
- MenuEventArgs and event properties
Styling & Customization
📄 Read: references/styling-and-customization.md
- Animation settings and effects (FadeIn, SlideDown, ZoomIn, None)
- CSS customization and class targeting
- Icon styling with iconCss property
- URL navigation and external links
- Scrollable menus (enableScrolling)
- Responsive design and Theme Studio
Templates & Advanced Features
📄 Read: references/templates-and-advanced.md
- Custom item templates (itemTemplate)
- Rich content with HTML and tables
- Character underlining and formatting
- Separator items and grouping
- Accessibility and keyboard navigation
- Advanced template patterns
Quick Start Example
import { Component } from '@angular/core';
import { ContextMenuModule } from '@syncfusion/ej2-angular-navigations';
import { MenuItemModel } from '@syncfusion/ej2-navigations';
@Component({
selector: 'app-root',
standalone: true,
imports: [ContextMenuModule],
template: `
<div class="e-section-control">
<!-- Target element for context menu -->
<div id="target">Right click / Touch hold to open the ContextMenu</div>
<!-- ContextMenu component -->
<ejs-contextmenu
id='contextmenu'
target='#target'
[items]='menuItems'>
</ejs-contextmenu>
</div>
`
})
export class AppComponent {
public menuItems: MenuItemModel[] = [
{ text: 'Cut', iconCss: 'e-cm-icons e-cut' },
{ text: 'Copy', iconCss: 'e-cm-icons e-copy' },
{ text: 'Paste', iconCss: 'e-cm-icons e-paste' },
{ separator: true },
{
text: 'View',
items: [
{ text: 'Large icons' },
{ text: 'Small icons' }
]
}
];
}Common Patterns
Pattern 1: Dynamic Item Management
// Add items after 'Refresh'
this.contextmenu.insertAfter([{ text: 'Sort By' }], 'Refresh');
// Remove 'Paste' item
this.contextmenu.removeItems(['Paste']);
// Disable 'Edit' item
this.contextmenu.enableItems(['Edit'], false);Pattern 2: Context-Aware Menus
beforeOpen(args: BeforeOpenCloseMenuEventArgs) {
if ((args.event.target as HTMLElement).id === 'editor') {
this.contextmenu.showItems(['Add', 'Edit', 'Delete']);
this.contextmenu.hideItems(['Cut', 'Copy', 'Paste']);
}
}Pattern 3: Menu Item Click Handler
itemSelect(args: MenuEventArgs): void {
if (args.item.text === 'Save As...') {
this.dialogComponent.show();
}
}Pattern 4: Animation Configuration
public animationSettings = {
effect: 'FadeIn',
duration: 400,
easing: 'ease'
};Complete API Reference
Component Properties
Core Configuration Properties
| Property | Type | Default | Description |
|---|---|---|---|
target | string | '' | Required. Specifies target element selector in which the ContextMenu should be opened. |
items | MenuItemModel[] | [] | Specifies menu items with its properties which will be rendered as ContextMenu. |
showItemOnClick | boolean | false | Specifies whether to show the sub menu or not on click. When true, the sub menu will open only on mouse click. |
filter | string | '' | Specifies the filter selector for elements inside the target in that the context menu will be opened. |
hoverDelay | number | 0 | If hoverDelay is set by particular number, the menu will open after that period (in milliseconds). |
Styling & Appearance Properties
| Property | Type | Default | Description |
|---|---|---|---|
animationSettings | MenuAnimationSettingsModel | { duration: 400, easing: 'ease', effect: 'SlideDown' } | Specifies the animation settings for the sub menu open/close. See Animation Settings section. |
cssClass | string | '' | Defines class/multiple classes separated by a space in the Menu wrapper. Use for custom styling. |
enableRtl | boolean | false | Enable or disable rendering component in right to left direction. |
Data & Behavior Properties
| Property | Type | Default | Description |
|---|---|---|---|
itemTemplate | `string \ | Function` | null |
locale | string | '' | Overrides the global culture and localization value for this component. Default global culture is 'en-US'. |
enableScrolling | boolean | false | Specifies whether to enable/disable the scrollable option in ContextMenu. |
Security & Persistence Properties
| Property | Type | Default | Description |
|---|---|---|---|
enableHtmlSanitizer | boolean | true | Specifies whether to enable the rendering of untrusted HTML values. If true, the component will sanitize any suspected untrusted strings and scripts before rendering them. Set to false only when you trust the HTML source completely. |
enablePersistence | boolean | false | Enable or disable persisting component's state between page reloads. When enabled, menu state is saved in localStorage. |
Property Example - Basic Configuration
Brief Example:
@Component({
selector: 'app-context-menu',
template: `
<div id="target">Right click here</div>
<ejs-contextmenu
target='#target'
[items]='items'
[animationSettings]='animSettings'
cssClass='custom-menu'>
</ejs-contextmenu>
`
})
export class ContextMenuComponent {
items: MenuItemModel[] = [
{ text: 'Edit' },
{ text: 'Delete' }
];
animSettings = {
duration: 300,
effect: 'FadeIn',
easing: 'ease-out'
};
}Full Working Example:
import { Component, ViewChild } from '@angular/core';
import { ContextMenuComponent } from '@syncfusion/ej2-angular-navigations';
import { MenuItemModel } from '@syncfusion/ej2-navigations';
@Component({
selector: 'app-context-menu-config',
standalone: true,
imports: [ContextMenuModule],
template: `
<div class="container">
<h3>Advanced Configuration Example</h3>
<!-- Target element for context menu -->
<div id="configTarget" class="target-box">
Right click or touch hold to open menu
</div>
<!-- ContextMenu with complete configuration -->
<ejs-contextmenu
#contextMenu
id='contextMenu'
target='#configTarget'
[items]='menuItems'
[animationSettings]='animationSettings'
cssClass='modern-menu'
[showItemOnClick]='true'
[enableScrolling]='true'
[hoverDelay]='300'
[enableHtmlSanitizer]='true'
[enablePersistence]='true'
locale='en-US'>
</ejs-contextmenu>
</div>
`,
styles: [`
.target-box {
width: 300px;
height: 200px;
border: 2px dashed #ccc;
display: flex;
align-items: center;
justify-content: center;
background: #f5f5f5;
cursor: context-menu;
}
`]
})
export class AdvancedContextMenuComponent {
@ViewChild('contextMenu') contextMenu!: ContextMenuComponent;
menuItems: MenuItemModel[] = [
{ text: 'Edit', iconCss: 'e-cm-icons e-edit', id: 'edit' },
{ text: 'Delete', iconCss: 'e-cm-icons e-delete', id: 'delete' },
{ separator: true },
{ text: 'More', id: 'more' }
];
animationSettings = {
effect: 'FadeIn' as any,
duration: 400,
easing: 'ease-in-out'
};
}Menu Item Properties (MenuItemModel)
MenuItemModel defines the structure for each menu item. Each item in the items array follows this model:
| Property | Type | Optional | Description |
|---|---|---|---|
text | string | No | Required. Specifies text for menu item. This is the display label shown to users. |
id | string | Yes | Specifies the id for menu item. Use this for identifying items in methods like enableItems(), removeItems(), etc. |
items | MenuItemModel[] | Yes | Specifies the sub menu items that is the array of MenuItem model. Creates nested/hierarchical menus. |
separator | boolean | Yes | Specifies separator between the menu items. Separators are either horizontal or vertical lines used to group menu items. Set to true to create a visual divider. |
iconCss | string | Yes | Defines class/multiple classes separated by a space for the menu Item that is used to include an icon. Menu Item can include font icon and sprite image. Example: iconCss: 'e-icons e-edit'. |
url | string | Yes | Specifies url for menu item that creates the anchor link to navigate to the url provided. When clicked, navigates to this URL. |
htmlAttributes | Record<string, any> | Yes | Specifies the htmlAttributes property to support adding custom attributes to the menu items. Example: { 'data-info': 'value', 'title': 'My Tooltip' }. |
MenuItem Example - Basic Usage
Brief Example:
items: MenuItemModel[] = [
{ text: 'Cut', id: 'cut', iconCss: 'e-icons e-cut' },
{ text: 'Copy', id: 'copy', iconCss: 'e-icons e-copy' },
{ text: 'Paste', id: 'paste', iconCss: 'e-icons e-paste' },
{ separator: true },
{ text: 'Delete', id: 'delete', iconCss: 'e-icons e-delete' }
];Full Working Example - Complex MenuItemModel:
@Component({
selector: 'app-menu-items-config',
standalone: true,
imports: [ContextMenuModule],
template: `
<div id="target">Right click for advanced menu</div>
<ejs-contextmenu target='#target' [items]='menuItems'></ejs-contextmenu>
`
})
export class MenuItemsComponent {
menuItems: MenuItemModel[] = [
// Item with icon
{
text: 'Edit',
id: 'edit-item',
iconCss: 'e-icons e-edit',
htmlAttributes: {
'data-action': 'edit',
'title': 'Edit selected item'
}
},
// Item with submenu
{
text: 'Format',
id: 'format',
iconCss: 'e-icons e-palette',
items: [
{ text: 'Bold', id: 'bold' },
{ text: 'Italic', id: 'italic' },
{ text: 'Underline', id: 'underline' }
]
},
// Navigation item with URL
{
text: 'Visit Website',
id: 'website',
iconCss: 'e-icons e-export',
url: 'https://example.com',
htmlAttributes: { 'target': '_blank' }
},
// Separator
{ separator: true },
// Item with nested submenu
{
text: 'Advanced',
id: 'advanced',
items: [
{
text: 'Settings',
id: 'settings',
items: [
{ text: 'General', id: 'general' },
{ text: 'Advanced', id: 'adv' }
]
},
{ text: 'Help', id: 'help', url: 'https://help.example.com' }
]
}
];
}Component Methods
Method: enableItems
Signature:
enableItems(items: string[], enable?: boolean, isUniqueId?: boolean): void| Parameter | Type | Optional | Description |
|---|---|---|---|
items | string[] | No | Array of item text or ids that needs to be enabled/disabled. |
enable | boolean | Yes, default: true | Set true to enable items; set false to disable items. |
isUniqueId | boolean | Yes, default: false | Set true if items array contains unique ids instead of text. |
Return: void
Brief Example:
// Disable 'Delete' item by text
this.contextMenu.enableItems(['Delete'], false);
// Enable items by id
this.contextMenu.enableItems(['edit-item', 'copy-item'], true, true);Full Working Example:
@Component({
selector: 'app-enable-items',
template: `
<button (click)="disableDelete()">Disable Delete</button>
<button (click)="enableAll()">Enable All</button>
<div id="target">Right click here</div>
<ejs-contextmenu #cm target='#target' [items]='items'></ejs-contextmenu>
`
})
export class EnableItemsComponent {
@ViewChild('cm') contextMenu!: ContextMenuComponent;
items: MenuItemModel[] = [
{ text: 'Edit', id: 'edit' },
{ text: 'Delete', id: 'delete' },
{ text: 'Copy', id: 'copy' }
];
disableDelete() {
this.contextMenu.enableItems(['Delete'], false);
}
enableAll() {
this.contextMenu.enableItems(['Edit', 'Delete', 'Copy'], true);
}
}Method: insertAfter
Signature:
insertAfter(items: MenuItemModel[], text: string, isUniqueId?: boolean): void| Parameter | Type | Optional | Description |
|---|---|---|---|
items | MenuItemModel[] | No | Array of MenuItemModel that needs to be inserted. |
text | string | No | Text item after which the element to be inserted. If isUniqueId is true, this is the unique id. |
isUniqueId | boolean | Yes, default: false | Set true if text is a unique id instead of display text. |
Return: void
Brief Example:
// Insert item after 'Edit'
this.contextMenu.insertAfter([{ text: 'Save' }], 'Edit');
// Insert after item with specific id
this.contextMenu.insertAfter([{ text: 'New', id: 'new-item' }], 'edit-item', true);Full Working Example:
@Component({
selector: 'app-insert-after',
template: `
<button (click)="addAfterEdit()">Add Item After Edit</button>
<div id="target">Right click here</div>
<ejs-contextmenu #cm target='#target' [items]='items'></ejs-contextmenu>
`
})
export class InsertAfterComponent {
@ViewChild('cm') contextMenu!: ContextMenuComponent;
items: MenuItemModel[] = [
{ text: 'Edit', id: 'edit' },
{ text: 'Delete', id: 'delete' }
];
addAfterEdit() {
this.contextMenu.insertAfter(
[
{ text: 'Copy', id: 'copy' },
{ text: 'Paste', id: 'paste' }
],
'Edit'
);
}
}Method: insertBefore
Signature:
insertBefore(items: MenuItemModel[], text: string, isUniqueId?: boolean): void| Parameter | Type | Optional | Description |
|---|---|---|---|
items | MenuItemModel[] | No | Array of MenuItemModel that needs to be inserted. |
text | string | No | Text item before which the element to be inserted. If isUniqueId is true, this is the unique id. |
isUniqueId | boolean | Yes, default: false | Set true if text is a unique id instead of display text. |
Return: void
Brief Example:
this.contextMenu.insertBefore([{ text: 'Undo' }], 'Edit');Method: removeItems
Signature:
removeItems(items: string[], isUniqueId?: boolean): void| Parameter | Type | Optional | Description |
|---|---|---|---|
items | string[] | No | Array of item text or ids that needs to be removed. |
isUniqueId | boolean | Yes, default: false | Set true if items array contains unique ids instead of text. |
Return: void
Brief Example:
this.contextMenu.removeItems(['Delete', 'Copy']);Method: hideItems
Signature:
hideItems(items: string[], isUniqueId?: boolean): void| Parameter | Type | Optional | Description |
|---|---|---|---|
items | string[] | No | Array of item text or ids that needs to be hidden. |
isUniqueId | boolean | Yes, default: false | Set true if items array contains unique ids instead of text. |
Return: void
Brief Example:
this.contextMenu.hideItems(['AdminOnly'], true);Method: showItems
Signature:
showItems(items: string[], isUniqueId?: boolean): void| Parameter | Type | Optional | Description |
|---|---|---|---|
items | string[] | No | Array of item text or ids that needs to be shown. |
isUniqueId | boolean | Yes, default: false | Set true if items array contains unique ids instead of text. |
Return: void
Brief Example:
this.contextMenu.showItems(['AdminOnly'], true);Method: getItemIndex
Signature:
getItemIndex(item: MenuItem | string, isUniqueId?: boolean): number[]| Parameter | Type | Optional | Description |
|---|---|---|---|
item | `MenuItem \ | string` | No |
isUniqueId | boolean | Yes, default: false | Set true if item is a unique id instead of text. |
Return: number[] - Array of indices representing the position of the item in the menu hierarchy.
Brief Example:
// Get index by text
const index = this.contextMenu.getItemIndex('Edit'); // [0] for first item
// Get index by id
const index = this.contextMenu.getItemIndex('edit-item', true); // [0]
// For nested items
const index = this.contextMenu.getItemIndex('Bold', true); // [1, 0] for Format > BoldFull Working Example:
@Component({
selector: 'app-get-index',
template: `
<button (click)="findItem()">Find Item Index</button>
<div>Index: {{ itemIndex }}</div>
<div id="target">Right click here</div>
<ejs-contextmenu #cm target='#target' [items]='items'></ejs-contextmenu>
`
})
export class GetIndexComponent {
@ViewChild('cm') contextMenu!: ContextMenuComponent;
itemIndex: any = null;
items: MenuItemModel[] = [
{ text: 'Edit', id: 'edit' },
{
text: 'Format',
id: 'format',
items: [
{ text: 'Bold', id: 'bold' },
{ text: 'Italic', id: 'italic' }
]
}
];
findItem() {
this.itemIndex = this.contextMenu.getItemIndex('Bold', true);
console.log('Item index:', this.itemIndex); // [1, 0]
}
}Method: setItem
Signature:
setItem(item: MenuItem, id?: string, isUniqueId?: boolean): void| Parameter | Type | Optional | Description |
|---|---|---|---|
item | MenuItem | No | MenuItem object containing updated properties. |
id | string | Yes | id or text of the item to be updated. If not provided, updates the item passed as first parameter. |
isUniqueId | boolean | Yes, default: false | Set true if id is a unique id instead of text. |
Return: void
Brief Example:
// Update item by text
this.contextMenu.setItem({ text: 'Edit Document' }, 'Edit');
// Update item by id
this.contextMenu.setItem(
{ text: 'Remove', iconCss: 'e-icons e-delete' },
'delete-item',
true
);Method: open
Signature:
open(top: number, left: number, target?: HTMLElement): void| Parameter | Type | Optional | Description |
|---|---|---|---|
top | number | No | To specify ContextMenu vertical positioning (Y coordinate in pixels). |
left | number | No | To specify ContextMenu horizontal positioning (X coordinate in pixels). |
target | HTMLElement | Yes | To calculate z-index for ContextMenu based upon the specified target element. |
Return: void
Brief Example:
// Open at mouse position
this.contextMenu.open(event.clientY, event.clientX);
// Open at specific coordinates
this.contextMenu.open(200, 300);
// Open relative to target element
this.contextMenu.open(100, 150, document.getElementById('target'));Method: close
Signature:
close(): void| Parameter | - | - | - |
|---|
Return: void
Brief Example:
// Close the open ContextMenu
this.contextMenu.close();Method: destroy
Signature:
destroy(): void| Parameter | - | - | - |
|---|
Return: void
Brief Example:
// Completely destroy the component and cleanup resources
this.contextMenu.destroy();Full Working Example - Destroy on Component Destroy:
import { Component, ViewChild, OnDestroy } from '@angular/core';
import { ContextMenuComponent } from '@syncfusion/ej2-angular-navigations';
@Component({
selector: 'app-destroy-example',
template: `
<button (click)="destroyMenu()">Destroy Menu</button>
<div id="target">Right click here</div>
<ejs-contextmenu #cm target='#target' [items]='items'></ejs-contextmenu>
`
})
export class DestroyExampleComponent implements OnDestroy {
@ViewChild('cm') contextMenu!: ContextMenuComponent;
items: MenuItemModel[] = [{ text: 'Item 1' }];
destroyMenu() {
if (this.contextMenu) {
this.contextMenu.destroy();
console.log('Menu destroyed');
}
}
ngOnDestroy() {
// Cleanup when component is destroyed
if (this.contextMenu) {
this.contextMenu.destroy();
}
}
}Animation Settings (MenuAnimationSettingsModel)
Configure how menu items animate when opening/closing:
| Property | Type | Default | Description |
|---|---|---|---|
duration | number | 400 | Specifies the time duration (in milliseconds) to transform/animate the menu. Example: 300 for 300ms animation. |
easing | string | 'ease' | Specifies the easing effect applied while transform. Examples: 'ease', 'ease-in', 'ease-out', 'ease-in-out', 'linear', 'cubic-bezier(0.25, 0.1, 0.25, 1)'. |
effect | MenuEffect | 'SlideDown' | Specifies the effect that shown in the sub menu transform. Options: `'None' \ |
Effect Options:
- None: Specifies the sub menu transform with no animation effect.
- SlideDown: Specifies the sub menu transform with slide down effect (default).
- ZoomIn: Specifies the sub menu transform with zoom in effect.
- FadeIn: Specifies the sub menu transform with fade in effect.
Brief Example:
animationSettings = {
duration: 300,
effect: 'FadeIn' as any,
easing: 'ease-out'
};Full Working Example:
@Component({
selector: 'app-animation-config',
template: `
<div id="target">Right click here</div>
<ejs-contextmenu
target='#target'
[items]='items'
[animationSettings]='animSettings'>
</ejs-contextmenu>
`
})
export class AnimationConfigComponent {
items: MenuItemModel[] = [
{ text: 'Option 1' },
{ text: 'Option 2' }
];
animSettings = {
effect: 'ZoomIn' as any,
duration: 200,
easing: 'ease-in-out'
};
}Component Events
Event: beforeOpen
Signature: (beforeOpen): EmitType<BeforeOpenCloseMenuEventArgs>
Triggers before opening the menu item. Use this to prevent menu opening, show/hide items based on context, or customize menu before display.
Event Arguments - BeforeOpenCloseMenuEventArgs:
| Property | Type | Description |
|---|---|---|
name | string | Specifies name of the event (value: 'beforeOpen'). |
Brief Example:
onBeforeOpen(args: BeforeOpenCloseMenuEventArgs) {
console.log('Menu about to open');
}Full Working Example:
@Component({
selector: 'app-before-open',
template: `
<div id="target">Right click to open</div>
<ejs-contextmenu
target='#target'
[items]='items'
(beforeOpen)='onBeforeOpen($event)'>
</ejs-contextmenu>
`
})
export class BeforeOpenComponent {
items: MenuItemModel[] = [{ text: 'Item 1' }];
onBeforeOpen(args: BeforeOpenCloseMenuEventArgs) {
console.log('Event:', args.name); // 'beforeOpen'
}
}Event: beforeClose
Signature: (beforeClose): EmitType<BeforeOpenCloseMenuEventArgs>
Triggers before closing the menu. Use this to perform cleanup or prevent menu from closing.
Event Arguments - BeforeOpenCloseMenuEventArgs:
| Property | Type | Description |
|---|---|---|
name | string | Specifies name of the event (value: 'beforeClose'). |
Brief Example:
onBeforeClose(args: BeforeOpenCloseMenuEventArgs) {
console.log('Menu about to close');
}Event: onOpen
Signature: (onOpen): EmitType<OpenCloseMenuEventArgs>
Triggers while opening the menu item. This event fires after the menu has been opened and is visible.
Event Arguments - OpenCloseMenuEventArgs:
| Property | Type | Description |
|---|---|---|
name | string | Specifies name of the event (value: 'onOpen'). |
Brief Example:
onOpen(args: OpenCloseMenuEventArgs) {
console.log('Menu opened:', args.name);
}Full Working Example:
@Component({
selector: 'app-open-event',
template: `
<div id="target">Right click here</div>
<ejs-contextmenu
target='#target'
[items]='items'
(onOpen)='onOpen($event)'
(onClose)='onClose($event)'>
</ejs-contextmenu>
<p>Status: {{ menuStatus }}</p>
`
})
export class OpenEventComponent {
items: MenuItemModel[] = [{ text: 'Item 1' }];
menuStatus = 'Closed';
onOpen(args: OpenCloseMenuEventArgs) {
this.menuStatus = 'Menu Opened - ' + args.name;
}
onClose(args: OpenCloseMenuEventArgs) {
this.menuStatus = 'Menu Closed - ' + args.name;
}
}Event: onClose
Signature: (onClose): EmitType<OpenCloseMenuEventArgs>
Triggers while closing the menu. This event fires after the menu has been closed and is no longer visible.
Event Arguments - OpenCloseMenuEventArgs:
| Property | Type | Description |
|---|---|---|
name | string | Specifies name of the event (value: 'onClose'). |
Brief Example:
onClose(args: OpenCloseMenuEventArgs) {
console.log('Menu closed:', args.name);
}Event: select
Signature: (select): EmitType<MenuEventArgs>
Triggers while selecting menu item. Use this to handle menu item clicks and perform actions.
Event Arguments - MenuEventArgs:
| Property | Type | Description |
|---|---|---|
name | string | Specifies name of the event (value: 'select'). |
Brief Example:
onSelect(args: MenuEventArgs) {
console.log('Item selected:', args.name);
}Full Working Example:
@Component({
selector: 'app-select-event',
template: `
<div id="target">Right click here</div>
<ejs-contextmenu
target='#target'
[items]='items'
(select)='onSelect($event)'>
</ejs-contextmenu>
<p>Selected: {{ selectedItem }}</p>
`
})
export class SelectEventComponent {
items: MenuItemModel[] = [
{ text: 'Edit', id: 'edit' },
{ text: 'Delete', id: 'delete' },
{ text: 'Copy', id: 'copy' }
];
selectedItem = 'None';
onSelect(args: MenuEventArgs) {
this.selectedItem = args.name;
console.log('Selected item event:', args.name);
}
}Event: beforeItemRender
Signature: (beforeItemRender): EmitType<MenuEventArgs>
Triggers while rendering each menu item. Use this to customize each item's appearance or behavior before rendering.
Event Arguments - MenuEventArgs:
| Property | Type | Description |
|---|---|---|
name | string | Specifies name of the event (value: 'beforeItemRender'). |
Brief Example:
onBeforeItemRender(args: MenuEventArgs) {
console.log('Rendering item:', args.name);
}Full Working Example:
@Component({
selector: 'app-before-item-render',
template: `
<div id="target">Right click here</div>
<ejs-contextmenu
target='#target'
[items]='items'
(beforeItemRender)='onBeforeItemRender($event)'>
</ejs-contextmenu>
`
})
export class BeforeItemRenderComponent {
items: MenuItemModel[] = [
{ text: 'Edit', id: 'edit' },
{ text: 'Delete', id: 'delete' }
];
onBeforeItemRender(args: MenuEventArgs) {
console.log('Rendering:', args.name);
// Customize items before rendering
}
}Event: created
Signature: (created): EmitType<Event>
Triggers once the component rendering is completed. Use this for post-initialization setup.
Event Arguments:
| Property | Type | Description |
|---|---|---|
| - | Event | Standard JavaScript Event object. |
Brief Example:
onCreated(event: Event) {
console.log('ContextMenu component created and ready');
}---
Key Properties & Events Quick Reference
| Property/Event | Purpose | Example |
|---|---|---|
items | Define menu structure | items: MenuItemModel[] |
target | Element triggering menu | target='#target' |
select | Menu item click | (select)="onSelect($event)" |
beforeOpen | Before menu opens | (beforeOpen)="onBeforeOpen($event)" |
enableItems() | Toggle item availability | enableItems(['Edit'], false) |
insertAfter() | Add items after target | insertAfter([...], 'Refresh') |
hideItems() | Hide specific items | hideItems(['Cut', 'Copy']) |
animationSettings | Control appearance effects | animationSettings: {...} |
Common Use Cases
1. File Operations Menu - Cut, Copy, Paste, Delete options 2. Content Editor Menu - Format, Insert, Link, Media options 3. Data Grid Context - Edit, Delete, Export, Filter actions 4. Navigation Menu - Links to pages or external URLs 5. Permission-Based Menu - Show/hide items based on user role 6. Multi-Language Menu - Dynamic item text from translations 7. Confirmation Dialogs - Open dialog before executing action 8. Table Operations - Row/column management in data tables
Next Steps
1. Select a reference file based on your task (Getting Started, Menu Management, Data Binding, Events, Styling, or Templates) 2. Review the code examples for your specific use case 3. Copy relevant code patterns to your project 4. Customize menu items for your application context 5. Test interactions with different target elements
---
Need help with a specific task? Reference the appropriate guide above and let me know which menu feature you're implementing.
Data Binding
Table of Contents
- MenuItemModel Structure
- Local Data Source Binding
- Parent-Child Item Relationships
- beforeItemRender Event
- Dynamic Data Updates
MenuItemModel Structure
The MenuItemModel interface defines the structure for menu items. Each item in the menu's items array follows this model:
Complete Property Reference Table
| Property | Type | Optional | Default | Description |
|---|---|---|---|---|
text | string | No | - | Required. Specifies text for menu item. This is the display label shown to users. |
id | string | Yes | - | Specifies the id for menu item. Use this for identifying items in methods like enableItems(), removeItems(), etc. Useful with isUniqueId parameter in component methods. |
items | MenuItemModel[] | Yes | - | Specifies the sub menu items that is the array of MenuItem model. Creates nested/hierarchical menus. Can have unlimited nesting levels. |
separator | boolean | Yes | false | Specifies separator between the menu items. Separators are either horizontal or vertical lines used to group menu items. Set to true to create a visual divider. |
iconCss | string | Yes | - | Defines class/multiple classes separated by a space for the menu Item that is used to include an icon. Menu Item can include font icon and sprite image. Example: iconCss: 'e-icons e-edit' or iconCss: 'fa-solid fa-pen'. |
url | string | Yes | - | Specifies url for menu item that creates the anchor link to navigate to the url provided. When clicked, navigates to this URL. Can be relative or absolute URLs. Example: /dashboard or https://example.com/page. |
htmlAttributes | Record<string, any> | Yes | - | Specifies the htmlAttributes property to support adding custom attributes to the menu items. Example: { 'data-info': 'value', 'title': 'My Tooltip', 'aria-label': 'Edit Item' }. Useful for custom styling, data attributes, and accessibility. |
Complete Property Example
Brief Example:
const menuItem: MenuItemModel = {
text: 'Edit', // Display text
id: 'edit-item', // Unique identifier
iconCss: 'e-icons e-edit', // Icon styling
items: [ // Sub-items (nested)
{ text: 'Undo' },
{ text: 'Redo' }
],
separator: false, // Not a separator
url: '', // No navigation
htmlAttributes: { // Custom attributes
'data-action': 'edit',
'title': 'Edit the item'
}
};Full Working Example - All Properties:
import { Component } from '@angular/core';
import { MenuItemModel } from '@syncfusion/ej2-navigations';
@Component({
selector: 'app-full-menuitem-example',
template: `
<div id="target">Right click here</div>
<ejs-contextmenu target='#target' [items]='menuItems'></ejs-contextmenu>
`
})
export class FullMenuItemExampleComponent {
menuItems: MenuItemModel[] = [
// Simple text item
{
text: 'New',
id: 'new-item'
},
// Item with icon
{
text: 'Edit',
id: 'edit-item',
iconCss: 'e-icons e-edit'
},
// Item with submenu
{
text: 'Format',
id: 'format-item',
iconCss: 'e-icons e-palette',
items: [
{ text: 'Bold', id: 'bold' },
{ text: 'Italic', id: 'italic' },
{ text: 'Underline', id: 'underline' }
]
},
// Separator divider
{
separator: true
},
// URL navigation item
{
text: 'Visit Website',
id: 'website-item',
iconCss: 'e-icons e-export',
url: 'https://example.com',
htmlAttributes: {
'target': '_blank',
'title': 'Open external link'
}
},
// Item with custom HTML attributes
{
text: 'Settings',
id: 'settings-item',
iconCss: 'e-icons e-settings',
htmlAttributes: {
'data-action': 'settings',
'data-premium': 'true',
'aria-label': 'Open settings menu',
'title': 'Configure options'
}
},
// Complex nested structure
{
text: 'Advanced',
id: 'advanced-item',
items: [
{
text: 'Development',
id: 'dev',
items: [
{ text: 'Console', id: 'console' },
{ text: 'Debugger', id: 'debugger' }
]
},
{
text: 'Analytics',
id: 'analytics',
url: '/analytics'
}
]
}
];
}Property Details and Examples
Property: text (Required)
Specifies the display text for the menu item. This is the only required property.
Brief Example:
{ text: 'Edit' }Full Example:
const items: MenuItemModel[] = [
{ text: 'Save' }, // Simple text
{ text: 'Save As...' }, // Text with ellipsis
{ text: 'Recent Files' }, // Submenu text
{ text: 'A' }, // Single character
{ text: 'Copy (Ctrl+C)' } // Text with shortcut hint
];Property: id (Optional)
Specifies the unique identifier for the menu item. Use this when you need to reference items programmatically in component methods.
Brief Example:
{ text: 'Edit', id: 'edit-item' }Full Example - Using IDs with Component Methods:
export class IdExampleComponent {
items: MenuItemModel[] = [
{ text: 'Edit', id: 'edit-item' },
{ text: 'Delete', id: 'delete-item' },
{ text: 'Copy', id: 'copy-item' }
];
@ViewChild('contextMenu') contextMenu!: ContextMenuComponent;
disableAdminActions() {
// Disable by ID (more reliable than by text)
this.contextMenu.enableItems(['delete-item', 'admin-settings'], false, true);
}
showSuperUserMenu() {
// Show items by ID
this.contextMenu.showItems(['admin-panel', 'audit-logs'], true);
}
findItemIndex() {
// Get index by ID
const index = this.contextMenu.getItemIndex('edit-item', true);
console.log('Edit item at index:', index);
}
}Property: iconCss (Optional)
Defines CSS classes for displaying an icon next to the menu item text. Supports Syncfusion icons, Font Awesome, or custom icon fonts.
Brief Example:
{ text: 'Edit', iconCss: 'e-icons e-edit' }Full Example - Various Icon Sources:
items: MenuItemModel[] = [
// Syncfusion built-in icons
{ text: 'Edit', iconCss: 'e-icons e-edit' },
{ text: 'Delete', iconCss: 'e-icons e-delete' },
{ text: 'Save', iconCss: 'e-icons e-save' },
{ text: 'Copy', iconCss: 'e-icons e-copy' },
// Font Awesome icons (if available)
{ text: 'Settings', iconCss: 'fa-solid fa-gear' },
{ text: 'User', iconCss: 'fa-solid fa-user' },
// Multiple icon classes
{ text: 'Premium Feature', iconCss: 'e-icons e-star custom-premium-icon' },
// Submenu with icons
{
text: 'Format',
iconCss: 'e-icons e-palette',
items: [
{ text: 'Bold', iconCss: 'e-icons e-bold' },
{ text: 'Italic', iconCss: 'e-icons e-italic' },
{ text: 'Underline', iconCss: 'e-icons e-underline' }
]
}
];CSS for Custom Icons:
.custom-premium-icon::before {
content: '★';
color: gold;
}Property: separator (Optional)
Creates a visual divider line between menu items for grouping related items.
Brief Example:
{ separator: true }Full Example - Logical Grouping:
items: MenuItemModel[] = [
// Group 1: File operations
{ text: 'New', iconCss: 'e-icons e-new' },
{ text: 'Open', iconCss: 'e-icons e-open' },
{ text: 'Save', iconCss: 'e-icons e-save' },
// Separator
{ separator: true },
// Group 2: Editing operations
{ text: 'Cut', iconCss: 'e-icons e-cut' },
{ text: 'Copy', iconCss: 'e-icons e-copy' },
{ text: 'Paste', iconCss: 'e-icons e-paste' },
// Separator
{ separator: true },
// Group 3: Document operations
{ text: 'Print', iconCss: 'e-icons e-print' },
{ text: 'Export', iconCss: 'e-icons e-export' }
];Property: url (Optional)
Specifies a URL that the menu item navigates to when clicked. Transforms the item into a clickable link.
Brief Example:
{ text: 'Google', url: 'https://google.com' }Full Example - Navigation:
items: MenuItemModel[] = [
// External URLs
{
text: 'Visit Syncfusion',
url: 'https://syncfusion.com',
htmlAttributes: { 'target': '_blank' }
},
// Internal routes
{
text: 'Dashboard',
url: '/dashboard',
iconCss: 'e-icons e-home'
},
{
text: 'Settings',
url: '/settings',
iconCss: 'e-icons e-settings'
},
// Submenu with URLs
{
text: 'Documentation',
items: [
{ text: 'Getting Started', url: '/docs/getting-started' },
{ text: 'API Reference', url: '/docs/api' },
{ text: 'Tutorials', url: 'https://tutorials.example.com' }
]
}
];Note: URL items still fire the select event, allowing you to intercept navigation if needed:
onSelect(args: MenuEventArgs) {
if (args.item.url === 'https://example.com') {
// Custom handling before navigation
console.log('Navigating to external link');
}
}Property: items (Optional - Nested MenuItemModel[])
Specifies sub-menu items, creating hierarchical/nested menus. Each item can have its own items array, allowing unlimited nesting depth.
Brief Example:
{
text: 'Format',
items: [
{ text: 'Bold' },
{ text: 'Italic' }
]
}Full Example - Hierarchical Structure:
items: MenuItemModel[] = [
{
text: 'File',
iconCss: 'e-icons e-folder',
items: [
{ text: 'New', id: 'file-new' },
{ text: 'Open', id: 'file-open' },
{
text: 'Recent Files',
id: 'recent',
items: [
{ text: 'Document1.txt', url: '/recent/doc1' },
{ text: 'Document2.txt', url: '/recent/doc2' },
{ separator: true },
{ text: 'Clear Recent', id: 'clear-recent' }
]
},
{ separator: true },
{ text: 'Exit', id: 'file-exit' }
]
},
{
text: 'Edit',
iconCss: 'e-icons e-edit',
items: [
{ text: 'Undo', id: 'edit-undo' },
{ text: 'Redo', id: 'edit-redo' },
{ separator: true },
{ text: 'Cut', id: 'edit-cut' },
{ text: 'Copy', id: 'edit-copy' },
{ text: 'Paste', id: 'edit-paste' }
]
},
{
text: 'View',
items: [
{
text: 'Zoom',
items: [
{ text: '100%', id: 'zoom-100' },
{ text: '150%', id: 'zoom-150' },
{ text: '200%', id: 'zoom-200' }
]
},
{
text: 'Layout',
items: [
{ text: 'Default', id: 'layout-default' },
{ text: 'Wide', id: 'layout-wide' },
{ text: 'Compact', id: 'layout-compact' }
]
}
]
}
];Programming Nested Items:
export class NestedProgrammingComponent {
addSubMenu() {
// Add submenu to existing item
const fileItem = this.menuItems[0];
if (fileItem.items) {
fileItem.items.push({
text: 'New Group',
items: [
{ text: 'Project', id: 'new-project' },
{ text: 'File', id: 'new-file' }
]
});
}
}
getNestedItemIndex() {
// Find nested item: Format > Bold
const index = this.contextMenu.getItemIndex('bold', true);
console.log(index); // [1, 0] - second level menu, first item
}
}Property: htmlAttributes (Optional - Record<string, any>)
Adds custom HTML attributes to the menu item DOM element. Useful for custom data storage, accessibility, and styling.
Brief Example:
{
text: 'Edit',
htmlAttributes: {
'data-action': 'edit',
'title': 'Edit selected item'
}
}Full Example - Various Use Cases:
items: MenuItemModel[] = [
// Custom data attributes
{
text: 'Edit',
id: 'edit-item',
htmlAttributes: {
'data-action': 'edit',
'data-icon': 'pencil',
'data-hotkey': 'Ctrl+E'
}
},
// Accessibility attributes
{
text: 'Delete',
htmlAttributes: {
'aria-label': 'Delete the selected file',
'role': 'menuitem',
'title': 'Permanently remove this item (cannot be undone)'
}
},
// External URL with target
{
text: 'Help',
url: 'https://help.example.com',
htmlAttributes: {
'target': '_blank',
'rel': 'noopener noreferrer',
'title': 'Open help in new tab'
}
},
// Permission-based styling
{
text: 'Admin Panel',
htmlAttributes: {
'data-permission': 'admin',
'data-requires-auth': 'true',
'class': 'admin-only',
'style': 'color: red;'
}
},
// Performance tracking
{
text: 'Download Report',
htmlAttributes: {
'data-track': 'true',
'data-event': 'report-download',
'data-category': 'exports'
}
},
// Nested items with attributes
{
text: 'Export',
htmlAttributes: {
'data-submenu': 'export-options'
},
items: [
{
text: 'PDF',
htmlAttributes: {
'data-format': 'pdf',
'data-size': 'small'
}
},
{
text: 'Excel',
htmlAttributes: {
'data-format': 'xlsx',
'data-size': 'medium'
}
}
]
}
];Accessing Custom Attributes in TypeScript:
onItemSelect(args: MenuEventArgs) {
// Access custom data attributes
const action = args.element.getAttribute('data-action');
const requiresAuth = args.element.getAttribute('data-requires-auth');
console.log('Action:', action);
console.log('Requires authentication:', requiresAuth);
// Track analytics
const trackData = args.element.getAttribute('data-track');
if (trackData === 'true') {
const event = args.element.getAttribute('data-event');
// Send to analytics
console.log('Track event:', event);
}
}CSS Styling with htmlAttributes:
/* Style admin-only items */
.admin-only {
color: red;
font-weight: bold;
}
/* Highlight items with data-premium attribute */
[data-premium="true"] {
background-color: #fff3cd;
border-left: 3px solid gold;
}
/* Add visual indicator for items requiring authentication */
[data-requires-auth="true"]::after {
content: ' 🔒';
}Core Properties
The basic MenuItemModel interface structure:
import { MenuItemModel } from '@syncfusion/ej2-navigations';
// Minimal required structure
interface MenuItemModel {
text: string; // Display text (required)
// Optional properties for enhancement
id?: string;
items?: MenuItemModel[];
separator?: boolean;
iconCss?: string;
url?: string;
htmlAttributes?: Record<string, any>;
}Local Data Source Binding
Array of Objects
Bind menu items from a simple array of objects:
import { Component } from '@angular/core';
import { ContextMenuModule } from '@syncfusion/ej2-angular-navigations';
import { MenuItemModel } from '@syncfusion/ej2-navigations';
@Component({
selector: 'app-root',
standalone: true,
imports: [ContextMenuModule],
template: `
<div class="e-section-control">
<div id="target">Right click / Touch hold to open the ContextMenu</div>
<ejs-contextmenu
target='#target'
[items]='menuItems'>
</ejs-contextmenu>
</div>
`
})
export class AppComponent {
public menuItems: MenuItemModel[] = [
{ text: 'Cut', iconCss: 'e-cm-icons e-cut' },
{ text: 'Copy', iconCss: 'e-cm-icons e-copy' },
{ text: 'Paste', iconCss: 'e-cm-icons e-paste' }
];
}Dynamic Array Creation
Create menu items programmatically from data:
export class AppComponent {
public menuItems: MenuItemModel[] = [];
constructor() {
this.initializeMenu();
}
initializeMenu(): void {
const actions = ['Cut', 'Copy', 'Paste', 'Delete'];
this.menuItems = actions.map(action => ({
text: action,
iconCss: `e-icons e-${action.toLowerCase()}`
}));
}
}Data from API/Service
Fetch menu items from an external source:
import { Component, OnInit } from '@angular/core';
import { MenuItemModel } from '@syncfusion/ej2-navigations';
// Mock service for demonstration
class MenuService {
getMenuItems(): MenuItemModel[] {
return [
{ text: 'Dashboard', iconCss: 'e-icons e-home' },
{ text: 'Settings', iconCss: 'e-icons e-settings' },
{ text: 'Logout', iconCss: 'e-icons e-close' }
];
}
}
@Component({
selector: 'app-root',
standalone: true,
template: `<ejs-contextmenu [items]='menuItems'></ejs-contextmenu>`
})
export class AppComponent implements OnInit {
public menuItems: MenuItemModel[] = [];
constructor(private menuService: MenuService) {}
ngOnInit(): void {
this.menuItems = this.menuService.getMenuItems();
}
}Parent-Child Item Relationships
Hierarchical Structure
Create multi-level menus with parent-child relationships:
public menuItems: MenuItemModel[] = [
{
text: 'File',
iconCss: 'e-icons e-folder',
items: [
{ text: 'New' },
{ text: 'Open' },
{ text: 'Save' },
{ text: 'Recent Files', items: [
{ text: 'document1.txt' },
{ text: 'document2.txt' }
]}
]
},
{
text: 'Edit',
iconCss: 'e-icons e-edit',
items: [
{ text: 'Undo' },
{ text: 'Redo' },
{ text: 'Cut' },
{ text: 'Copy' },
{ text: 'Paste' }
]
}
];Building from Flat Data
Transform flat data structure into hierarchical menu:
interface MenuItem {
id: number;
parentId?: number;
text: string;
icon?: string;
}
const flatData: MenuItem[] = [
{ id: 1, text: 'File', icon: 'e-folder' },
{ id: 2, parentId: 1, text: 'New' },
{ id: 3, parentId: 1, text: 'Open' },
{ id: 4, text: 'Edit', icon: 'e-edit' },
{ id: 5, parentId: 4, text: 'Cut' },
{ id: 6, parentId: 4, text: 'Copy' }
];
export class AppComponent {
public menuItems: MenuItemModel[] = [];
constructor() {
this.buildHierarchy();
}
buildHierarchy(): void {
const items: MenuItemModel[] = [];
const itemMap = new Map<number, MenuItemModel>();
// First pass: create all items
flatData.forEach(data => {
const menuItem: MenuItemModel = {
text: data.text,
iconCss: data.icon ? `e-icons ${data.icon}` : undefined,
items: []
};
itemMap.set(data.id, menuItem);
});
// Second pass: build hierarchy
flatData.forEach(data => {
const menuItem = itemMap.get(data.id)!;
if (data.parentId) {
const parent = itemMap.get(data.parentId);
if (parent && parent.items) {
parent.items.push(menuItem);
}
} else {
items.push(menuItem);
}
});
this.menuItems = items;
}
}Managing Parent-Child Updates
updateSubmenu(parentId: string, newSubItems: MenuItemModel[]): void {
const parent = this.menuItems.find(m => m.id === parentId);
if (parent) {
parent.items = newSubItems;
// Trigger menu refresh if needed
}
}beforeItemRender Event
Item Rendering Customization
The beforeItemRender event fires before each menu item renders, allowing customization:
import { Component } from '@angular/core';
import { ContextMenuModule, MenuEventArgs } from '@syncfusion/ej2-angular-navigations';
import { MenuItemModel } from '@syncfusion/ej2-navigations';
@Component({
selector: 'app-root',
standalone: true,
imports: [ContextMenuModule],
template: `
<div class="e-section-control">
<div id="target">Right click / Touch hold</div>
<ejs-contextmenu
target='#target'
[items]='menuItems'
(beforeItemRender)='itemRender($event)'>
</ejs-contextmenu>
</div>
`
})
export class AppComponent {
public menuItems: MenuItemModel[] = [
{ text: 'Cut' },
{ text: 'Copy' },
{ text: 'Paste' }
];
itemRender(args: MenuEventArgs): void {
// Called before each item renders
console.log('Rendering item:', args.item.text);
}
}Conditional Item Styling
itemRender(args: MenuEventArgs): void {
// Highlight disabled items
if (args.item.disabled) {
args.element.classList.add('disabled-item');
}
// Add CSS classes based on item properties
if (args.item.iconCss) {
args.element.classList.add('icon-item');
}
}Custom HTML Content
itemRender(args: MenuEventArgs): void {
// Customize item display with HTML
if (args.item.text === 'Save As...') {
args.element.innerHTML = `
<span>Save As...</span>
<span style="color: gray; font-size: 0.9em;">(Ctrl+S)</span>
`;
}
}Data Transformation
itemRender(args: MenuEventArgs): void {
// Format text based on item properties
if (args.item.text) {
// Convert to title case
const titleCase = args.item.text
.split(' ')
.map(w => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase())
.join(' ');
args.element.textContent = titleCase;
}
}Handling Separator Items
itemRender(args: MenuEventArgs): void {
// Add custom styling to separators
if (!args.item.text && args.item.separator) {
args.element.classList.add('custom-separator');
}
}Dynamic Data Updates
Updating Menu Items at Runtime
Modify menu items and refresh the component:
export class AppComponent {
public menuItems: MenuItemModel[] = [
{ text: 'New' },
{ text: 'Open' },
{ text: 'Save' }
];
addMenuItem(newItem: MenuItemModel): void {
this.menuItems.push(newItem);
// Trigger change detection
}
updateMenuItem(index: number, updatedItem: MenuItemModel): void {
this.menuItems[index] = updatedItem;
}
removeMenuItem(index: number): void {
this.menuItems.splice(index, 1);
}
}Real-Time Data Binding
Bind menu items to a data source that changes:
export class AppComponent implements OnInit {
public menuItems: MenuItemModel[] = [];
private dataRefreshInterval: any;
ngOnInit(): void {
this.loadMenuItems();
// Refresh menu items every 5 seconds
this.dataRefreshInterval = setInterval(() => {
this.loadMenuItems();
}, 5000);
}
private loadMenuItems(): void {
// Fetch fresh data from service
this.menuItems = [
{ text: 'Item 1', id: '1' },
{ text: 'Item 2', id: '2' },
{ text: 'Item 3', id: '3' }
];
}
ngOnDestroy(): void {
if (this.dataRefreshInterval) {
clearInterval(this.dataRefreshInterval);
}
}
}Conditional Item Rendering
public get dynamicMenuItems(): MenuItemModel[] {
const items: MenuItemModel[] = [
{ text: 'New', iconCss: 'e-icons e-new' },
{ text: 'Open', iconCss: 'e-icons e-open' }
];
// Add admin-only items
if (this.isAdmin) {
items.push({ text: 'Settings', iconCss: 'e-icons e-settings' });
items.push({ text: 'Manage Users', iconCss: 'e-icons e-users' });
}
return items;
}Batch Updates
updateMenuStructure(newStructure: MenuItemModel[]): void {
this.menuItems = []; // Clear existing
// Add new items in batch
newStructure.forEach(item => {
this.menuItems.push(item);
});
}Complete Example: Dynamic Data-Bound Menu
import { Component, OnInit, ViewChild } from '@angular/core';
import { ContextMenuComponent, ContextMenuModule, MenuEventArgs } from '@syncfusion/ej2-angular-navigations';
import { MenuItemModel } from '@syncfusion/ej2-navigations';
interface DataRecord {
id: number;
text: string;
iconCss?: string;
subItems?: DataRecord[];
}
@Component({
selector: 'app-root',
standalone: true,
imports: [ContextMenuModule],
template: `
<div class="e-section-control">
<button (click)="addItem()">Add Item</button>
<button (click)="refreshData()">Refresh Data</button>
<div id="target">Right click / Touch hold</div>
<ejs-contextmenu
#contextmenu
target='#target'
[items]='menuItems'
(beforeItemRender)='onItemRender($event)'>
</ejs-contextmenu>
</div>
`
})
export class AppComponent implements OnInit {
@ViewChild('contextmenu')
public contextmenu?: ContextMenuComponent;
public menuItems: MenuItemModel[] = [];
private dataSource: DataRecord[] = [];
ngOnInit(): void {
this.loadDataSource();
this.bindMenuItems();
}
private loadDataSource(): void {
this.dataSource = [
{
id: 1,
text: 'File',
iconCss: 'e-icons e-folder',
subItems: [
{ id: 11, text: 'New' },
{ id: 12, text: 'Open' },
{ id: 13, text: 'Save' }
]
},
{
id: 2,
text: 'Edit',
iconCss: 'e-icons e-edit',
subItems: [
{ id: 21, text: 'Cut' },
{ id: 22, text: 'Copy' },
{ id: 23, text: 'Paste' }
]
}
];
}
private bindMenuItems(): void {
this.menuItems = this.transformData(this.dataSource);
}
private transformData(data: DataRecord[]): MenuItemModel[] {
return data.map(record => ({
text: record.text,
iconCss: record.iconCss,
id: record.id.toString(),
items: record.subItems ? this.transformData(record.subItems) : undefined
}));
}
onItemRender(args: MenuEventArgs): void {
// Apply custom styling
if (args.item.iconCss) {
args.element.classList.add('has-icon');
}
}
addItem(): void {
const newItem: DataRecord = {
id: this.dataSource.length + 1,
text: 'New Item',
iconCss: 'e-icons e-plus'
};
this.dataSource.push(newItem);
this.bindMenuItems();
}
refreshData(): void {
this.loadDataSource();
this.bindMenuItems();
}
}---
Next: Handle menu item clicks and events in references/interaction-and-events.md.
Getting Started with Syncfusion Angular ContextMenu
Table of Contents
- Installation and Dependencies
- Angular Environment Setup
- Creating Your First ContextMenu
- Target Element Configuration
- Basic Menu Item Structure
Installation and Dependencies
Required Packages
The ContextMenu component requires the following Syncfusion packages:
@syncfusion/ej2-angular-navigations
├── @syncfusion/ej2-angular-base
└── @syncfusion/ej2-navigations
├── @syncfusion/ej2-base
├── @syncfusion/ej2-data
├── @syncfusion/ej2-lists
├── @syncfusion/ej2-inputs
├── @syncfusion/ej2-splitbuttons
└── @syncfusion/ej2-popups
└── @syncfusion/ej2-buttonsInstallation Command
Install the package using npm:
npm install @syncfusion/ej2-angular-navigationsAngular Environment Setup
Prerequisites
- Node.js and npm installed
- Angular CLI installed globally
npm install -g @angular/cliCreating a New Angular Application
Generate a new Angular application with Angular CLI:
ng new syncfusion-angular-appWhen prompted, configure:
- Routing: Choose as needed for your project
- Stylesheet format: CSS or SCSS
For modern Angular (21+) which uses standalone architecture:
ng new syncfusion-angular-app --style=scssNavigate to Project Directory
cd syncfusion-angular-appAngular 21 Standalone Architecture: Standalone components are the default in Angular 21+. This guide uses standalone architecture. Components no longer require NgModule declarations.
Creating Your First ContextMenu
Basic Component Setup
Create a new component with the ContextMenu:
import { Component } from '@angular/core';
import { ContextMenuModule } from '@syncfusion/ej2-angular-navigations';
import { MenuItemModel } from '@syncfusion/ej2-navigations';
import { enableRipple } from '@syncfusion/ej2-base';
enableRipple(true);
@Component({
selector: 'app-root',
standalone: true,
imports: [ContextMenuModule],
template: `
<div class="e-section-control">
<!-- Target element -->
<div id="target">Right click / Touch hold to open the ContextMenu</div>
<!-- ContextMenu component -->
<ejs-contextmenu
id='contextmenu'
target='#target'
[items]='menuItems'>
</ejs-contextmenu>
</div>
`,
styles: [`
#target {
height: 200px;
width: 300px;
border: 1px solid #ccc;
padding: 20px;
display: flex;
align-items: center;
justify-content: center;
cursor: context-menu;
background-color: #f5f5f5;
}
`]
})
export class AppComponent {
public menuItems: MenuItemModel[] = [
{ text: 'Back' },
{ text: 'Forward' },
{ text: 'Refresh' },
{ separator: true },
{ text: 'Save As...' },
{ text: 'Print' }
];
}Standalone Bootstrap
Configure the main entry point:
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));Running the Application
Start the development server:
ng serveNavigate to http://localhost:4200/ in your browser. Right-click or touch-hold the target area to see the context menu appear.
Target Element Configuration
Single Target Element
Configure a single element as the context menu trigger:
<ejs-contextmenu
target='#myElement'
[items]='menuItems'>
</ejs-contextmenu>Multiple Target Elements (Same Class)
Use CSS selectors to target multiple elements:
<div class="target-area">Right click me</div>
<div class="target-area">Or me</div>
<ejs-contextmenu
target='.target-area'
[items]='menuItems'>
</ejs-contextmenu>Nested Target Elements
Target nested elements using descendant selectors:
<div id="container">
<div class="item">Item 1</div>
<div class="item">Item 2</div>
</div>
<ejs-contextmenu
target='#container .item'
[items]='menuItems'>
</ejs-contextmenu>Dynamic Target Assignment
Assign target via component property:
@Component({
template: `
<ejs-contextmenu
[target]='targetElement'
[items]='menuItems'>
</ejs-contextmenu>
`
})
export class AppComponent {
public targetElement = '#target';
}Basic Menu Item Structure
MenuItem Properties
Each menu item is defined using the MenuItemModel interface:
import { MenuItemModel } from '@syncfusion/ej2-navigations';
public menuItems: MenuItemModel[] = [
{
text: 'Cut', // Display text
iconCss: 'e-cut-icon', // Icon CSS class
id: 'cut-item' // Unique identifier
},
{
text: 'Copy',
iconCss: 'e-copy-icon'
},
{
separator: true // Separator line
},
{
text: 'View',
items: [ // Nested sub-items
{ text: 'Large Icons' },
{ text: 'Small Icons' }
]
}
];Simple Text Items
Basic menu items with just text:
public menuItems: MenuItemModel[] = [
{ text: 'New' },
{ text: 'Open' },
{ text: 'Save' }
];Items with Icons
Add visual icons using CSS classes:
public menuItems: MenuItemModel[] = [
{ text: 'Cut', iconCss: 'e-cm-icons e-cut' },
{ text: 'Copy', iconCss: 'e-cm-icons e-copy' },
{ text: 'Paste', iconCss: 'e-cm-icons e-paste' }
];Items with Separators
Use separators to group related items:
public menuItems: MenuItemModel[] = [
{ text: 'Cut' },
{ text: 'Copy' },
{ separator: true }, // Separator line
{ text: 'Save' },
{ text: 'Print' }
];Nested Multi-Level Menus
Create hierarchical menus with sub-items:
public menuItems: MenuItemModel[] = [
{
text: 'File',
items: [
{ text: 'New' },
{ text: 'Open' },
{ text: 'Save' }
]
},
{
text: 'Edit',
items: [
{ text: 'Cut' },
{ text: 'Copy' },
{ text: 'Paste' }
]
}
];Complete Example with All Features
import { Component } from '@angular/core';
import { ContextMenuModule } from '@syncfusion/ej2-angular-navigations';
import { MenuItemModel } from '@syncfusion/ej2-navigations';
@Component({
selector: 'app-root',
standalone: true,
imports: [ContextMenuModule],
template: `
<div class="e-section-control">
<div id="target">Right click / Touch hold</div>
<ejs-contextmenu
id='contextmenu'
target='#target'
[items]='menuItems'>
</ejs-contextmenu>
</div>
`
})
export class AppComponent {
public menuItems: MenuItemModel[] = [
{ text: 'New', iconCss: 'e-icons e-new' },
{ text: 'Open', iconCss: 'e-icons e-open' },
{ separator: true },
{
text: 'View',
items: [
{ text: 'Large Icons' },
{ text: 'Small Icons' }
]
},
{ separator: true },
{ text: 'Refresh', iconCss: 'e-icons e-refresh' }
];
}Common Issues & Solutions
Issue: Context menu doesn't appear on right-click
- Solution: Verify the
targetelement exists in DOM and the selector is correct
Issue: Styling looks different from Syncfusion samples
- Solution: Import Syncfusion CSS files (usually auto-imported, but check in styles.css)
Issue: Menu items not displaying icons
- Solution: Ensure
iconCssuses valid Syncfusion icon classes (e.g.,e-icons e-cut)
Issue: Sub-menus not appearing
- Solution: Verify nested items are properly defined in the
itemsproperty
Issue: "Cannot find module @syncfusion/ej2-angular-navigations"
- Solution: Run
npm install @syncfusion/ej2-angular-navigationsand verify package.json
---
Next: Proceed to references/menu-items-management.md to learn how to dynamically add, remove, and manage menu items.
Interaction & Events
Table of Contents
- Menu Item Click Handlers
- Click-to-Open Submenus
- Programmatic Open and Close
- Menu Positioning
- Opening Dialogs on Item Selection
- MenuEventArgs Reference
Menu Item Click Handlers
select Event
Handle menu item clicks using the select event:
import { Component } from '@angular/core';
import { ContextMenuModule, MenuEventArgs } from '@syncfusion/ej2-angular-navigations';
import { MenuItemModel } from '@syncfusion/ej2-navigations';
@Component({
selector: 'app-root',
standalone: true,
imports: [ContextMenuModule],
template: `
<div class="e-section-control">
<div id="target">Right click / Touch hold to open the ContextMenu</div>
<ejs-contextmenu
target='#target'
[items]='menuItems'
(select)='onSelect($event)'>
</ejs-contextmenu>
<p>Selected Item: {{ selectedItem }}</p>
</div>
`
})
export class AppComponent {
public menuItems: MenuItemModel[] = [
{ text: 'Cut' },
{ text: 'Copy' },
{ text: 'Paste' }
];
public selectedItem = '';
onSelect(args: MenuEventArgs): void {
this.selectedItem = args.item.text || 'Unknown';
console.log('Item clicked:', args.item.text);
}
}Conditional Actions Based on Selection
onSelect(args: MenuEventArgs): void {
switch (args.item.text) {
case 'Cut':
this.performCut();
break;
case 'Copy':
this.performCopy();
break;
case 'Paste':
this.performPaste();
break;
}
}
private performCut(): void {
console.log('Cutting selected content');
// Implement cut logic
}
private performCopy(): void {
console.log('Copying selected content');
// Implement copy logic
}
private performPaste(): void {
console.log('Pasting content');
// Implement paste logic
}Accessing Event Arguments
onSelect(args: MenuEventArgs): void {
// Get item properties
const itemText = args.item.text;
const itemId = args.item.id;
const itemElement = args.element;
// Get event details
const eventSource = args.event;
const targetElement = args.event.target as HTMLElement;
console.log(`Selected: ${itemText} (ID: ${itemId})`);
console.log(`Target element ID: ${targetElement.id}`);
}Click-to-Open Submenus
showItemOnClick Property
By default, submenus open on hover. Enable click-based opening:
import { Component } from '@angular/core';
import { ContextMenuModule } from '@syncfusion/ej2-angular-navigations';
import { MenuItemModel } from '@syncfusion/ej2-navigations';
@Component({
selector: 'app-root',
standalone: true,
imports: [ContextMenuModule],
template: `
<div class="e-section-control">
<div id="target">Right click / Touch hold to open the ContextMenu</div>
<ejs-contextmenu
target='#target'
[items]='menuItems'
[showItemOnClick]='true'>
</ejs-contextmenu>
</div>
`
})
export class AppComponent {
public menuItems: MenuItemModel[] = [
{ text: 'Show All Bookmarks' },
{
text: 'Bookmarks Toolbar',
items: [
{
text: 'Most Visited',
items: [
{ text: 'Gmail' },
{ text: 'Google' }
]
},
{ text: 'Recently Added' }
]
}
];
}Advantages of Click-Based Submenus
- Prevents accidental submenu opening on hover
- Better for touch devices where hover isn't available
- More deliberate user interaction
- Reduces menu clutter on hover
Programmatic Open and Close
open() Method
Open the context menu programmatically at specific coordinates:
import { Component, ViewChild } from '@angular/core';
import { ContextMenuComponent, ContextMenuModule } from '@syncfusion/ej2-angular-navigations';
import { ButtonModule } from '@syncfusion/ej2-angular-buttons';
import { MenuItemModel } from '@syncfusion/ej2-navigations';
import { getInstance } from '@syncfusion/ej2-base';
import { ContextMenu } from '@syncfusion/ej2-navigations';
@Component({
selector: 'app-root',
standalone: true,
imports: [ContextMenuModule, ButtonModule],
template: `
<div class="e-section-control">
<button ejs-button (click)="openMenu()">Open ContextMenu</button>
<ejs-contextmenu
id='contextmenu'
[items]='menuItems'>
</ejs-contextmenu>
</div>
`
})
export class AppComponent {
public menuItems: MenuItemModel[] = [
{ text: 'Cut' },
{ text: 'Copy' },
{ text: 'Paste' }
];
openMenu(): void {
// Get reference to context menu component
const contextmenuElement = document.getElementById('contextmenu_0') as HTMLElement;
const contextmenuObj = getInstance(contextmenuElement, ContextMenu) as ContextMenu;
// Open at specific coordinates (top: 40px, left: 20px)
contextmenuObj.open(40, 20);
}
}close() Method
Close the context menu programmatically:
closeMenu(): void {
const contextmenuElement = document.getElementById('contextmenu_0') as HTMLElement;
const contextmenuObj = getInstance(contextmenuElement, ContextMenu) as ContextMenu;
contextmenuObj.close();
}Open at Mouse Position
openMenuAtMouse(event: MouseEvent): void {
const contextmenuElement = document.getElementById('contextmenu_0') as HTMLElement;
const contextmenuObj = getInstance(contextmenuElement, ContextMenu) as ContextMenu;
// Open at mouse cursor position
contextmenuObj.open(event.clientY, event.clientX);
}Menu Positioning
Top and Left Coordinates
Position the menu at specific screen coordinates:
openMenu(): void {
const contextmenuObj = getInstance(
document.getElementById('contextmenu_0'),
ContextMenu
) as ContextMenu;
// Position: 100px from top, 150px from left
contextmenuObj.open(100, 150);
}Relative to Target Element
openMenuAtElement(): void {
const targetElement = document.getElementById('target') as HTMLElement;
const rect = targetElement.getBoundingClientRect();
const contextmenuObj = getInstance(
document.getElementById('contextmenu_0'),
ContextMenu
) as ContextMenu;
// Position at element's bottom-right
contextmenuObj.open(
rect.top + rect.height,
rect.left + rect.width
);
}Center on Screen
centerMenuOnScreen(): void {
const contextmenuObj = getInstance(
document.getElementById('contextmenu_0'),
ContextMenu
) as ContextMenu;
const top = window.innerHeight / 2;
const left = window.innerWidth / 2;
contextmenuObj.open(top, left);
}Opening Dialogs on Item Selection
Dialog Integration
Open a dialog when a specific menu item is clicked:
import { Component, ViewChild } from '@angular/core';
import { ContextMenuModule, MenuEventArgs } from '@syncfusion/ej2-angular-navigations';
import { DialogModule, DialogComponent } from '@syncfusion/ej2-angular-popups';
import { MenuItemModel } from '@syncfusion/ej2-navigations';
@Component({
selector: 'app-root',
standalone: true,
imports: [ContextMenuModule, DialogModule],
template: `
<div class="e-section-control">
<div id="target">Right click / Touch hold to open the ContextMenu</div>
<ejs-dialog
#dialog
[visible]='dialogVisible'
[buttons]='dialogButtons'
header='Save As'
content='Enter file name...'>
</ejs-dialog>
<ejs-contextmenu
target="#target"
[items]="menuItems"
(select)="itemSelect($event)">
</ejs-contextmenu>
</div>
`
})
export class AppComponent {
@ViewChild('dialog')
public dialog?: DialogComponent;
public dialogVisible = false;
public menuItems: MenuItemModel[] = [
{ text: 'New' },
{ text: 'Open' },
{ text: 'Save As...' },
{ text: 'Print' }
];
public dialogButtons: any[] = [
{
buttonModel: {
isPrimary: true,
content: 'Save',
cssClass: 'e-flat'
},
click: () => this.onDialogSave()
}
];
itemSelect(args: MenuEventArgs): void {
if (args.item.text === 'Save As...') {
this.dialogVisible = true;
if (this.dialog) {
this.dialog.show();
}
}
}
onDialogSave(): void {
console.log('File saved');
this.dialogVisible = false;
if (this.dialog) {
this.dialog.hide();
}
}
}Confirmation Dialog
itemSelect(args: MenuEventArgs): void {
if (args.item.text === 'Delete') {
// Show confirmation dialog before deleting
const confirmDelete = confirm('Are you sure you want to delete this item?');
if (confirmDelete) {
this.performDelete();
}
}
}
private performDelete(): void {
console.log('Item deleted');
}Complete Event Reference
Event: select
Triggers: While selecting menu item (when item is clicked).
Signature: (select)='onSelect($event)'
Event Arguments - MenuEventArgs:
| Property | Type | Description |
|---|---|---|
name | string | Specifies name of the event. Value: 'select'. |
event | Event | Original browser event object (click event). Contains properties like type, timeStamp, target. |
item | MenuItemModel | The menu item that was selected/clicked. Contains all item properties (text, id, iconCss, etc.). |
element | HTMLElement | The DOM element of the menu item that was clicked. Can be used for styling or accessing attributes. |
MenuEventArgs Interface (Complete):
interface MenuEventArgs {
name: string; // 'select' | 'beforeItemRender'
event: Event; // Browser event
item: MenuItemModel; // Selected menu item
element: HTMLElement; // DOM element of item
element.textContent: string; // Item text
element.innerHTML: string; // Item HTML
element.classList: DOMTokenList; // CSS classes
}Brief Example:
onSelect(args: MenuEventArgs) {
console.log('Selected:', args.item.text);
}Full Example - Complete Event Handling:
onSelect(args: MenuEventArgs): void {
// Item information
const itemText = args.item.text;
const itemId = args.item.id;
const itemElement = args.element;
// Event information
const eventType = args.event.type; // e.g., 'click'
const timestamp = (args.event as any).timeStamp;
// Element information
const classes = itemElement.classList;
const html = itemElement.innerHTML;
// Event target (the element that triggered the menu)
const targetElement = args.event.target as HTMLElement;
console.log({
itemText,
itemId,
eventType,
timestamp,
classes: Array.from(classes),
targetElementId: targetElement.id
});
}Event: beforeOpen
Triggers: Before opening the menu item.
Signature: (beforeOpen)='onBeforeOpen($event)'
Event Arguments - BeforeOpenCloseMenuEventArgs:
| Property | Type | Description |
|---|---|---|
name | string | Specifies name of the event. Value: 'beforeOpen'. |
event | Event | Original browser event object (contextmenu or mousedown event) that triggered the menu opening. |
BeforeOpenCloseMenuEventArgs Interface (Complete):
interface BeforeOpenCloseMenuEventArgs {
name: string; // 'beforeOpen' | 'beforeClose'
event: Event; // Browser event that triggered opening/closing
// Additional properties may be available depending on Syncfusion version
}Brief Example:
onBeforeOpen(args: BeforeOpenCloseMenuEventArgs) {
console.log('Menu about to open');
}Full Example - Dynamic Menu Modification:
import { Component, ViewChild } from '@angular/core';
import { ContextMenuComponent, BeforeOpenCloseMenuEventArgs } from '@syncfusion/ej2-angular-navigations';
@Component({
selector: 'app-before-open',
template: `
<div id="target">Right click here</div>
<ejs-contextmenu
#cm
target='#target'
[items]='menuItems'
(beforeOpen)='onBeforeOpen($event)'>
</ejs-contextmenu>
`
})
export class BeforeOpenComponent {
@ViewChild('cm') contextMenu!: ContextMenuComponent;
menuItems: MenuItemModel[] = [
{ text: 'Edit', id: 'edit' },
{ text: 'Delete', id: 'delete' },
{ text: 'Admin Only', id: 'admin' }
];
isAdmin = false;
onBeforeOpen(args: BeforeOpenCloseMenuEventArgs): void {
console.log('Event name:', args.name); // 'beforeOpen'
// Access target element
const targetElement = args.event.target as HTMLElement;
console.log('Menu opening on element:', targetElement.id);
// Dynamically show/hide items based on context
if (this.isAdmin) {
this.contextMenu.showItems(['admin'], true);
} else {
this.contextMenu.hideItems(['admin'], true);
}
}
}Use Case - Context-Aware Menu:
onBeforeOpen(args: BeforeOpenCloseMenuEventArgs): void {
const targetElement = args.event.target as HTMLElement;
// Show different menu items based on target element
if (targetElement.classList.contains('protected')) {
this.contextMenu.hideItems(['Delete', 'Edit'], true);
} else {
this.contextMenu.showItems(['Delete', 'Edit'], true);
}
}Event: beforeClose
Triggers: Before closing the menu.
Signature: (beforeClose)='onBeforeClose($event)'
Event Arguments - BeforeOpenCloseMenuEventArgs:
| Property | Type | Description |
|---|---|---|
name | string | Specifies name of the event. Value: 'beforeClose'. |
event | Event | Browser event that triggered the closing. |
Brief Example:
onBeforeClose(args: BeforeOpenCloseMenuEventArgs) {
console.log('Menu about to close');
}Full Example - State Cleanup:
onBeforeClose(args: BeforeOpenCloseMenuEventArgs): void {
console.log('Cleaning up before menu closes');
// Save menu state
this.previousMenuState = 'closed';
// Reset highlight
this.highlightedItem = null;
}Event: onOpen
Triggers: While opening the menu item (after menu is displayed).
Signature: (onOpen)='onOpen($event)'
Event Arguments - OpenCloseMenuEventArgs:
| Property | Type | Description |
|---|---|---|
name | string | Specifies name of the event. Value: 'onOpen'. |
event | Event | Browser event that triggered the opening. |
OpenCloseMenuEventArgs Interface (Complete):
interface OpenCloseMenuEventArgs {
name: string; // 'onOpen' | 'onClose'
event: Event; // Browser event
// Additional properties may be available depending on Syncfusion version
}Brief Example:
onOpen(args: OpenCloseMenuEventArgs) {
console.log('Menu opened:', args.name);
}Full Example - Menu State Tracking:
@Component({
selector: 'app-menu-state',
template: `
<div>Menu Status: {{ menuStatus }}</div>
<div id="target">Right click here</div>
<ejs-contextmenu
target='#target'
[items]='items'
(onOpen)='onOpen($event)'
(onClose)='onClose($event)'>
</ejs-contextmenu>
`
})
export class MenuStateComponent {
menuStatus = 'Closed';
items: MenuItemModel[] = [{ text: 'Item 1' }];
onOpen(args: OpenCloseMenuEventArgs) {
this.menuStatus = 'Open - Event: ' + args.name;
console.log('Menu displayed at:', new Date().toLocaleTimeString());
}
onClose(args: OpenCloseMenuEventArgs) {
this.menuStatus = 'Closed - Event: ' + args.name;
}
}Event: onClose
Triggers: While closing the menu (after menu is hidden).
Signature: (onClose)='onClose($event)'
Event Arguments - OpenCloseMenuEventArgs:
| Property | Type | Description |
|---|---|---|
name | string | Specifies name of the event. Value: 'onClose'. |
event | Event | Browser event that triggered the closing. |
Brief Example:
onClose(args: OpenCloseMenuEventArgs) {
console.log('Menu closed:', args.name);
}Event: beforeItemRender
Triggers: While rendering each menu item (before each item is displayed).
Signature: (beforeItemRender)='onBeforeItemRender($event)'
Event Arguments - MenuEventArgs:
| Property | Type | Description |
|---|---|---|
name | string | Specifies name of the event. Value: 'beforeItemRender'. |
event | Event | Browser event associated with rendering. |
item | MenuItemModel | The menu item being rendered. |
element | HTMLElement | The DOM element of the item being rendered. |
Brief Example:
onBeforeItemRender(args: MenuEventArgs) {
console.log('Rendering item:', args.item.text);
}Full Example - Item Customization:
onBeforeItemRender(args: MenuEventArgs): void {
// Customize based on item properties
if (args.item.disabled) {
args.element.classList.add('disabled-item');
args.element.style.opacity = '0.5';
}
// Add custom styling to specific items
if (args.item.text === 'Delete') {
args.element.classList.add('danger-item');
args.element.style.color = 'red';
}
// Highlight admin items
if ((args.item as any).role === 'admin') {
args.element.classList.add('admin-badge');
args.element.setAttribute('data-admin', 'true');
}
}Use Case - Dynamic Content Rendering:
onBeforeItemRender(args: MenuEventArgs): void {
// Add keyboard shortcut hints
const shortcuts = {
'Cut': '(Ctrl+X)',
'Copy': '(Ctrl+C)',
'Paste': '(Ctrl+V)',
'Undo': '(Ctrl+Z)',
'Redo': '(Ctrl+Y)'
};
if (shortcuts[args.item.text!]) {
const shortcut = shortcuts[args.item.text!];
args.element.innerHTML += `<span style="float: right; color: gray; font-size: 0.9em;">${shortcut}</span>`;
}
}Event: created
Triggers: Once the component rendering is completed (after initialization).
Signature: (created)='onCreated()' or (created)='onCreated($event)'
Event Arguments:
| Property | Type | Description |
|---|---|---|
| - | Event | Standard JavaScript Event object. |
Brief Example:
onCreated() {
console.log('ContextMenu component created and ready');
}Full Example - Component Initialization:
import { Component, ViewChild, OnInit } from '@angular/core';
import { ContextMenuComponent } from '@syncfusion/ej2-angular-navigations';
@Component({
selector: 'app-created-event',
template: `
<div>Status: {{ componentStatus }}</div>
<div id="target">Right click here</div>
<ejs-contextmenu
#cm
target='#target'
[items]='items'
(created)='onCreated()'>
</ejs-contextmenu>
`
})
export class CreatedEventComponent implements OnInit {
@ViewChild('cm') contextMenu!: ContextMenuComponent;
items: MenuItemModel[] = [{ text: 'Item 1' }];
componentStatus = 'Initializing...';
ngOnInit() {
// Note: created event fires during initialization
}
onCreated() {
this.componentStatus = 'Ready';
console.log('Component fully initialized');
// Now safe to access component methods
console.log('Total items:', this.contextMenu.items?.length);
}
}Event Differences: before vs on Events
Understanding the timing of different events:
| Event | Timing | Purpose | Can Prevent |
|---|---|---|---|
beforeOpen | Before menu appears | Prepare menu, modify items | Yes (in some cases) |
onOpen | After menu is visible | React to menu opening | No |
beforeClose | Before menu disappears | Save state, cleanup | Yes (in some cases) |
onClose | After menu is hidden | React to menu closing | No |
select | When item clicked | Handle selection | Depends on item action |
beforeItemRender | Before each item renders | Customize appearance | No |
created | After component initialized | Setup component | No |
Visual Timeline:
Right-click occurs
↓
beforeOpen ← Can modify menu here
↓
[Menu displays]
↓
onOpen ← React to visible menu
↓
User clicks item
↓
select ← Handle selection
↓
beforeClose ← Cleanup before closing
↓
[Menu hides]
↓
onClose ← React to hidden menuMenuEventArgs Complete Reference
The MenuEventArgs object provides comprehensive event information when items are selected or rendered:
interface MenuEventArgs {
name: string; // Event name ('select' or 'beforeItemRender')
event: Event; // Browser event (click, render, etc.)
item: MenuItemModel; // Menu item involved in the event
{
text?: string; // Item display text
id?: string; // Item unique id
items?: MenuItemModel[]; // Sub-items
iconCss?: string; // Icon CSS class
url?: string; // Navigation URL
separator?: boolean; // Is separator
htmlAttributes?: Record<string, any>; // Custom HTML attributes
[key: string]: any; // Other properties
}
element: HTMLElement; // DOM element of the item
{
textContent: string; // Element text
innerHTML: string; // Element HTML
classList: DOMTokenList; // CSS classes
getAttribute(name: string): string; // Get attributes
setAttribute(name: string, value: string): void; // Set attributes
// ... other HTMLElement methods
}
}Practical Usage Examples
onSelect(args: MenuEventArgs): void {
// Item information
const itemText = args.item.text;
const itemId = args.item.id;
const itemElement = args.element;
// Event information
const eventType = args.event.type; // e.g., 'click'
const timestamp = (args.event as any).timeStamp;
// Element information
const classes = itemElement.classList;
const html = itemElement.innerHTML;
// Event target (the element that triggered the menu)
const targetElement = args.event.target as HTMLElement;
console.log({
itemText,
itemId,
eventType,
timestamp,
classes: Array.from(classes),
targetElementId: targetElement.id
});
}BeforeOpenCloseMenuEventArgs Complete Reference
The BeforeOpenCloseMenuEventArgs object is used for beforeOpen and beforeClose events:
interface BeforeOpenCloseMenuEventArgs {
name: string; // 'beforeOpen' or 'beforeClose'
event: Event; // Browser event that triggered the action
{
type: string; // Event type ('contextmenu', 'mousedown', etc.)
target: HTMLElement; // Element that triggered the event
preventDefault(): void; // Can prevent default behavior
stopPropagation(): void; // Can stop event bubbling
}
}OpenCloseMenuEventArgs Complete Reference
The OpenCloseMenuEventArgs object is used for onOpen and onClose events:
interface OpenCloseMenuEventArgs {
name: string; // 'onOpen' or 'onClose'
event: Event; // Browser event
}Complete Example: Interactive Menu with Events
import { Component, ViewChild } from '@angular/core';
import { ContextMenuComponent, ContextMenuModule, MenuEventArgs } from '@syncfusion/ej2-angular-navigations';
import { BeforeOpenCloseMenuEventArgs } from '@syncfusion/ej2-navigations';
import { MenuItemModel } from '@syncfusion/ej2-navigations';
@Component({
selector: 'app-root',
standalone: true,
imports: [ContextMenuModule],
template: `
<div class="e-section-control">
<div id="target">Right click / Touch hold</div>
<div style="margin-top: 20px;">
<p>Last Action: {{ lastAction }}</p>
<p>Menu State: {{ menuState }}</p>
</div>
<ejs-contextmenu
#contextmenu
target='#target'
[items]='menuItems'
(created)='onCreated()'
(beforeOpen)='onBeforeOpen($event)'
(select)='onSelect($event)'>
</ejs-contextmenu>
</div>
`
})
export class AppComponent {
@ViewChild('contextmenu')
public contextmenu?: ContextMenuComponent;
public menuItems: MenuItemModel[] = [
{ text: 'New', iconCss: 'e-icons e-new' },
{ text: 'Open', iconCss: 'e-icons e-open' },
{ separator: true },
{ text: 'Save', iconCss: 'e-icons e-save' },
{ text: 'Save As...' }
];
public lastAction = 'None';
public menuState = 'Closed';
onCreated(): void {
console.log('ContextMenu initialized');
this.lastAction = 'Component Created';
}
onBeforeOpen(args: BeforeOpenCloseMenuEventArgs): void {
this.menuState = 'Opening...';
console.log('Menu opening');
}
onSelect(args: MenuEventArgs): void {
this.lastAction = `Selected: ${args.item.text}`;
this.menuState = 'Closed';
console.log(`User clicked: ${args.item.text}`);
// Execute action based on selection
this.executeAction(args.item.text);
}
private executeAction(itemText?: string): void {
switch (itemText) {
case 'New':
console.log('Creating new file');
break;
case 'Open':
console.log('Opening file');
break;
case 'Save':
console.log('Saving file');
break;
case 'Save As...':
console.log('Saving file with new name');
break;
}
}
}---
Next: Customize menu appearance, animations, and styling in references/styling-and-customization.md.
Menu Items Management
Table of Contents
- Adding Menu Items Dynamically
- Removing Menu Items
- Enabling and Disabling Items
- Showing and Hiding Items
- Dynamic Context-Aware Menus
- Multi-Level Nested Menus
Adding Menu Items Dynamically
insertAfter() Method
Add new menu items after a specified target item:
import { Component, ViewChild } from '@angular/core';
import { ContextMenuComponent, ContextMenuModule, MenuItemModel } from '@syncfusion/ej2-angular-navigations';
@Component({
selector: 'app-root',
standalone: true,
imports: [ContextMenuModule],
template: `
<div class="e-section-control">
<div id="target">Right click / Touch hold to open the ContextMenu</div>
<ejs-contextmenu
#contextmenu
target='#target'
[items]='menuItems'
(created)='onCreated()'>
</ejs-contextmenu>
</div>
`
})
export class AppComponent {
@ViewChild('contextmenu')
public contextmenu?: ContextMenuComponent;
public menuItems: MenuItemModel[] = [
{ text: 'View' },
{ text: 'Refresh' },
{ text: 'New' }
];
onCreated(): void {
// Insert 'Sort By' after 'Refresh'
(this.contextmenu as ContextMenuComponent).insertAfter(
[{ text: 'Sort By' }],
'Refresh'
);
}
}insertBefore() Method
Add new menu items before a specified target item:
onCreated(): void {
// Insert 'Display Settings' before 'Personalize'
(this.contextmenu as ContextMenuComponent).insertBefore(
[{ text: 'Display Settings' }],
'Personalize'
);
}Multiple Items Insertion
Insert multiple items at once:
onCreated(): void {
const newItems: MenuItemModel[] = [
{ text: 'Sort By Name' },
{ text: 'Sort By Date' },
{ text: 'Sort By Size' }
];
(this.contextmenu as ContextMenuComponent).insertAfter(
newItems,
'Refresh'
);
}Removing Menu Items
removeItems() Method
Remove specified items from the context menu:
onCreated(): void {
// Remove single item by text
(this.contextmenu as ContextMenuComponent).removeItems(['Paste']);
}Remove Multiple Items
onCreated(): void {
const itemsToRemove = ['Cut', 'Copy', 'Paste'];
(this.contextmenu as ContextMenuComponent).removeItems(itemsToRemove);
}Remove Based on Condition
removeUnauthorizedItems(userRole: string): void {
if (userRole === 'viewer') {
(this.contextmenu as ContextMenuComponent).removeItems([
'Delete',
'Edit',
'Rename'
]);
}
}Enabling and Disabling Items
enableItems() Method
Control item availability based on application state:
import { Component, ViewChild } from '@angular/core';
import { ContextMenuComponent, ContextMenuModule, MenuItemModel } from '@syncfusion/ej2-angular-navigations';
@Component({
selector: 'app-root',
standalone: true,
imports: [ContextMenuModule],
template: `
<div class="e-section-control">
<div id="target">Right click / Touch hold to open the ContextMenu</div>
<ejs-contextmenu
#contextmenu
target='#target'
[items]='menuItems'
(created)='onCreated()'
(beforeOpen)='beforeOpen()'>
</ejs-contextmenu>
</div>
`
})
export class AppComponent {
@ViewChild('contextmenu')
public contextmenu?: ContextMenuComponent;
public menuItems: MenuItemModel[] = [
{
text: 'View',
items: [
{ text: 'Large icons' },
{ text: 'Medium icons' },
{ text: 'Small icons' }
]
},
{ text: 'Refresh' },
{ separator: true },
{ text: 'New' }
];
onCreated(): void {
// Disable parent item 'View'
(this.contextmenu as ContextMenuComponent).enableItems(
['View'],
false
);
}
beforeOpen(): void {
// Disable sub-item 'Medium icons' on menu open
(this.contextmenu as ContextMenuComponent).enableItems(
['Medium icons'],
false
);
}
}Re-enable Disabled Items
// Enable previously disabled items
(this.contextmenu as ContextMenuComponent).enableItems(
['Edit', 'Delete'],
true // enable = true
);Conditional Item State
updateItemState(canEdit: boolean): void {
(this.contextmenu as ContextMenuComponent).enableItems(
['Edit', 'Cut', 'Delete'],
canEdit
);
}Showing and Hiding Items
hideItems() Method
Hide menu items dynamically without removing them:
beforeOpen(args: BeforeOpenCloseMenuEventArgs) {
// Hide specific items
(this.contextmenu as ContextMenuComponent).hideItems([
'Cut',
'Copy',
'Paste'
]);
}showItems() Method
Display previously hidden items:
beforeOpen(args: BeforeOpenCloseMenuEventArgs) {
// Show specific items
(this.contextmenu as ContextMenuComponent).showItems([
'Save',
'Export'
]);
}Dynamic Context-Aware Menus
Show Different Menus Based on Target
Display different menu items depending on which element was right-clicked:
import { Component, ViewChild } from '@angular/core';
import { ContextMenuComponent, ContextMenuModule, MenuItemModel } from '@syncfusion/ej2-angular-navigations';
import { BeforeOpenCloseMenuEventArgs } from '@syncfusion/ej2-navigations';
@Component({
selector: 'app-root',
standalone: true,
imports: [ContextMenuModule],
template: `
<div class="e-section-control">
<div id="target">
<div id='clipboard' class='e-div'>Clipboard Area</div>
<div id='editor' class='e-div'>Editor Area</div>
</div>
<ejs-contextmenu
#contextmenu
target='#target .e-div'
[items]='menuItems'
(beforeOpen)='beforeOpen($event)'>
</ejs-contextmenu>
</div>
`,
styles: [`
.e-div {
padding: 20px;
border: 1px solid #ccc;
margin: 10px;
height: 100px;
}
`]
})
export class AppComponent {
@ViewChild('contextmenu')
public contextmenu?: ContextMenuComponent;
public menuItems: MenuItemModel[] = [
{ text: 'Cut' },
{ text: 'Copy' },
{ text: 'Paste' },
{ text: 'Add' },
{ text: 'Edit' },
{ text: 'Delete' }
];
beforeOpen(args: BeforeOpenCloseMenuEventArgs) {
const targetId = (args.event.target as HTMLElement).id;
if (targetId === 'clipboard') {
// Show clipboard operations
(this.contextmenu as ContextMenuComponent).showItems([
'Cut',
'Copy',
'Paste'
]);
(this.contextmenu as ContextMenuComponent).hideItems([
'Add',
'Edit',
'Delete'
]);
} else if (targetId === 'editor') {
// Show editor operations
(this.contextmenu as ContextMenuComponent).showItems([
'Add',
'Edit',
'Delete'
]);
(this.contextmenu as ContextMenuComponent).hideItems([
'Cut',
'Copy',
'Paste'
]);
}
}
}Role-Based Menu Items
beforeOpen(args: BeforeOpenCloseMenuEventArgs) {
const userRole = this.getUserRole();
if (userRole === 'admin') {
(this.contextmenu as ContextMenuComponent).showItems([
'Delete',
'Export',
'Manage Users'
]);
} else if (userRole === 'editor') {
(this.contextmenu as ContextMenuComponent).showItems([
'Edit',
'Copy'
]);
(this.contextmenu as ContextMenuComponent).hideItems(['Delete']);
} else {
(this.contextmenu as ContextMenuComponent).hideItems([
'Delete',
'Edit',
'Export'
]);
}
}
private getUserRole(): string {
// Fetch user role from service
return 'editor';
}Multi-Level Nested Menus
Creating Nested Structure
Define menu items with multiple nesting levels:
public menuItems: MenuItemModel[] = [
{
text: 'File',
items: [
{
text: 'New',
items: [
{ text: 'Document' },
{ text: 'Project' }
]
},
{ text: 'Open' },
{ text: 'Save' }
]
},
{
text: 'Edit',
items: [
{ text: 'Cut' },
{ text: 'Copy' },
{ text: 'Paste' }
]
}
];Managing Nested Items
Access and manipulate items at any level:
// Get reference to nested items
const viewItems = this.menuItems.find(m => m.text === 'View')?.items;
// Add nested item
this.contextmenu.insertAfter(
[{ text: 'Compact View' }],
'Large Icons'
);Dynamic Nested Menus
addDynamicSubmenu(parentItem: string, newSubItem: MenuItemModel): void {
const parent = this.menuItems.find(m => m.text === parentItem);
if (parent) {
if (!parent.items) {
parent.items = [];
}
parent.items.push(newSubItem);
}
}Example: Nested Submenu Filtering
beforeOpen(args: BeforeOpenCloseMenuEventArgs) {
// Disable certain sub-items based on conditions
if (this.isReadOnly) {
(this.contextmenu as ContextMenuComponent).enableItems(
['Delete', 'Edit'],
false
);
}
}Complete Example: Advanced Menu Management
import { Component, ViewChild } from '@angular/core';
import { ContextMenuComponent, ContextMenuModule, MenuItemModel, MenuEventArgs } from '@syncfusion/ej2-angular-navigations';
import { BeforeOpenCloseMenuEventArgs } from '@syncfusion/ej2-navigations';
@Component({
selector: 'app-root',
standalone: true,
imports: [ContextMenuModule],
template: `
<div class="e-section-control">
<button (click)="addItem()">Add Item</button>
<button (click)="removeItem()">Remove Item</button>
<button (click)="toggleItemState()">Toggle State</button>
<div id="target">Right click / Touch hold</div>
<ejs-contextmenu
#contextmenu
target='#target'
[items]='menuItems'
(created)='onCreated()'
(beforeOpen)='beforeOpen($event)'
(select)='onSelect($event)'>
</ejs-contextmenu>
</div>
`
})
export class AppComponent {
@ViewChild('contextmenu')
public contextmenu?: ContextMenuComponent;
public menuItems: MenuItemModel[] = [
{ text: 'New' },
{ text: 'Open' },
{ separator: true },
{ text: 'Edit' },
{ text: 'Delete' }
];
private isDeleteEnabled = true;
onCreated(): void {
console.log('Context menu created');
}
beforeOpen(args: BeforeOpenCloseMenuEventArgs): void {
// Dynamic state management
if (!this.isDeleteEnabled) {
(this.contextmenu as ContextMenuComponent).enableItems(['Delete'], false);
}
}
addItem(): void {
(this.contextmenu as ContextMenuComponent).insertAfter(
[{ text: 'Print' }],
'Open'
);
}
removeItem(): void {
(this.contextmenu as ContextMenuComponent).removeItems(['Print']);
}
toggleItemState(): void {
this.isDeleteEnabled = !this.isDeleteEnabled;
(this.contextmenu as ContextMenuComponent).enableItems(
['Delete'],
this.isDeleteEnabled
);
}
onSelect(args: MenuEventArgs): void {
console.log('Selected:', args.item.text);
}
}---
Next: Learn how to bind menu items from data sources in references/data-binding.md.
Styling & Customization
Table of Contents
Animation Settings
Supported Animation Effects
The ContextMenu supports four animation effects:
| Effect | Behavior |
|---|---|
| None | No animation, menu appears instantly |
| SlideDown | Menu slides down from top (default) |
| ZoomIn | Menu scales from small to full size |
| FadeIn | Menu fades in gradually |
Basic Animation Configuration
import { Component } from '@angular/core';
import { ContextMenuModule } from '@syncfusion/ej2-angular-navigations';
import { MenuItemModel, MenuAnimationSettingsModel } from '@syncfusion/ej2-navigations';
@Component({
selector: 'app-root',
standalone: true,
imports: [ContextMenuModule],
template: `
<div class="e-section-control">
<div id="target">Right click / Touch hold to open the ContextMenu</div>
<ejs-contextmenu
target='#target'
[items]='menuItems'
[animationSettings]='animationSettings'>
</ejs-contextmenu>
</div>
`
})
export class AppComponent {
public menuItems: MenuItemModel[] = [
{ text: 'New' },
{ text: 'Open' },
{ text: 'Save' }
];
public animationSettings: MenuAnimationSettingsModel = {
effect: 'FadeIn',
duration: 400
};
}Animation Options
// SlideDown effect (default)
public animationSettings1 = {
effect: 'SlideDown',
duration: 400,
easing: 'ease'
};
// ZoomIn effect
public animationSettings2 = {
effect: 'ZoomIn',
duration: 600,
easing: 'ease-out'
};
// FadeIn effect
public animationSettings3 = {
effect: 'FadeIn',
duration: 300,
easing: 'linear'
};
// No animation
public animationSettings4 = {
effect: 'None',
duration: 0
};Custom Duration and Easing
public animationSettings = {
effect: 'SlideDown',
duration: 800, // 800ms animation time
easing: 'ease-in-out'
};Complete Animation Example
@Component({
selector: 'app-root',
standalone: true,
imports: [ContextMenuModule],
template: `
<div class="e-section-control">
<label>Select Animation:</label>
<select [(ngModel)]='selectedEffect' (change)='updateAnimation()'>
<option>SlideDown</option>
<option>ZoomIn</option>
<option>FadeIn</option>
<option>None</option>
</select>
<div id="target">Right click / Touch hold</div>
<ejs-contextmenu
target='#target'
[items]='menuItems'
[animationSettings]='animationSettings'>
</ejs-contextmenu>
</div>
`
})
export class AppComponent {
public selectedEffect = 'SlideDown';
public animationSettings: MenuAnimationSettingsModel = {
effect: 'SlideDown',
duration: 400
};
public menuItems: MenuItemModel[] = [
{ text: 'New' },
{ text: 'Open' },
{ text: 'Save' }
];
updateAnimation(): void {
this.animationSettings.effect = this.selectedEffect as any;
}
}CSS Customization
CSS Classes Reference
Target specific menu elements with these classes:
/* Main wrapper */
.e-contextmenu-wrapper {
background-color: #fff;
border: 1px solid #ddd;
}
/* Menu item */
.e-contextmenu-wrapper .e-menu-parent {
color: #333;
}
/* Menu item on hover */
.e-contextmenu-wrapper ul .e-menu-item:hover {
background-color: #f0f0f0;
}
/* Selected item */
.e-contextmenu-wrapper ul .e-menu-item.e-selected {
background-color: #007bff;
color: #fff;
}
/* context menu caret icon */
.e-contextmenu-wrapper ul .e-menu-item.e-selected .e-caret::before {
color: inherit;
}
/* Caret/Arrow for submenus */
.e-contextmenu-wrapper ul .e-menu-item .e-menu-icon::before {
color: inherit;
}
/* Disabled item */
.e-contextmenu-wrapper ul .e-menu-item.e-disabled {
opacity: 0.5;
cursor: not-allowed;
}
/* Separator line */
.e-contextmenu-wrapper .e-separator {
border-bottom: 1px solid #ddd;
margin: 5px 0;
}Custom Theme Example
@Component({
selector: 'app-root',
standalone: true,
imports: [ContextMenuModule],
template: `
<div class="e-section-control">
<div id="target">Right click / Touch hold</div>
<ejs-contextmenu
target='#target'
[items]='menuItems'>
</ejs-contextmenu>
</div>
`,
styles: [`
:host ::ng-deep .e-contextmenu-wrapper {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}
:host ::ng-deep .e-contextmenu-wrapper ul .e-menu-item {
color: #fff;
padding: 12px 16px;
font-weight: 500;
}
:host ::ng-deep .e-contextmenu-wrapper ul .e-menu-item:hover {
background-color: rgba(255,255,255,0.2);
}
:host ::ng-deep .e-contextmenu-wrapper ul .e-menu-item.e-selected {
background-color: rgba(255,255,255,0.3);
}
:host ::ng-deep .e-separator {
background-color: rgba(255,255,255,0.3);
}
`]
})
export class AppComponent {
public menuItems: MenuItemModel[] = [
{ text: 'New' },
{ text: 'Open' },
{ separator: true },
{ text: 'Save' }
];
}Icon Styling
Adding Icons with iconCss
Use the iconCss property to add icons from Syncfusion icon library:
public menuItems: MenuItemModel[] = [
{ text: 'Cut', iconCss: 'e-cm-icons e-cut' },
{ text: 'Copy', iconCss: 'e-cm-icons e-copy' },
{ text: 'Paste', iconCss: 'e-cm-icons e-paste' },
{ separator: true },
{ text: 'Select All', iconCss: 'e-icons e-select-all' }
];Common Syncfusion Icons
// File operations
{ text: 'New', iconCss: 'e-icons e-new' }
{ text: 'Open', iconCss: 'e-icons e-open' }
{ text: 'Save', iconCss: 'e-icons e-save' }
{ text: 'Delete', iconCss: 'e-icons e-delete' }
// Edit operations
{ text: 'Cut', iconCss: 'e-cm-icons e-cut' }
{ text: 'Copy', iconCss: 'e-cm-icons e-copy' }
{ text: 'Paste', iconCss: 'e-cm-icons e-paste' }
{ text: 'Undo', iconCss: 'e-icons e-undo' }
{ text: 'Redo', iconCss: 'e-icons e-redo' }
// Navigation
{ text: 'Back', iconCss: 'e-icons e-back' }
{ text: 'Forward', iconCss: 'e-icons e-forward' }
{ text: 'Refresh', iconCss: 'e-icons e-refresh' }
// Other
{ text: 'Settings', iconCss: 'e-icons e-settings' }
{ text: 'Help', iconCss: 'e-icons e-help' }
{ text: 'Info', iconCss: 'e-icons e-info' }Custom Icon Classes
Define custom icon classes in your CSS:
@Component({
template: `<ejs-contextmenu [items]='menuItems'></ejs-contextmenu>`,
styles: [`
.custom-icon-download::before {
content: '⬇️';
}
.custom-icon-upload::before {
content: '⬆️';
}
.custom-icon-share::before {
content: '↗️';
}
`]
})
export class AppComponent {
public menuItems: MenuItemModel[] = [
{ text: 'Download', iconCss: 'custom-icon-download' },
{ text: 'Upload', iconCss: 'custom-icon-upload' },
{ text: 'Share', iconCss: 'custom-icon-share' }
];
}URL Navigation
Navigate to External URLs
Configure menu items to navigate to URLs:
import { Component } from '@angular/core';
import { ContextMenuModule, MenuEventArgs } from '@syncfusion/ej2-angular-navigations';
import { MenuItemModel } from '@syncfusion/ej2-navigations';
@Component({
selector: 'app-root',
standalone: true,
imports: [ContextMenuModule],
template: `
<div class="e-section-control">
<div id="target">Right click / Touch hold to open the ContextMenu</div>
<ejs-contextmenu
target='#target'
[items]='menuItems'
(beforeItemRender)='onBeforeItemRender($event)'>
</ejs-contextmenu>
</div>
`
})
export class AppComponent {
public menuItems: MenuItemModel[] = [
{ text: 'Flipkart', iconCss: 'e-cart-icon e-link', url: 'https://www.flipkart.com' },
{ text: 'Amazon', iconCss: 'e-cart-icon e-link', url: 'https://www.amazon.com' },
{ separator: true },
{ text: 'GitHub', iconCss: 'e-code-icon e-link', url: 'https://github.com' }
];
onBeforeItemRender(args: MenuEventArgs): void {
if (args.item.url) {
// Make URL items clickable
args.element.style.cursor = 'pointer';
}
}
}Navigate via Event Handler
onSelect(args: MenuEventArgs): void {
if (args.item.url) {
// Open in new tab
window.open(args.item.url, '_blank');
}
}Navigate to Routes
import { Router } from '@angular/router';
export class AppComponent {
constructor(private router: Router) {}
onSelect(args: MenuEventArgs): void {
switch (args.item.text) {
case 'Dashboard':
this.router.navigate(['/dashboard']);
break;
case 'Settings':
this.router.navigate(['/settings']);
break;
case 'Profile':
this.router.navigate(['/profile']);
break;
}
}
}Scrollable Menus
enableScrolling Property
Enable scrolling for menus with many items:
import { Component } from '@angular/core';
import { ContextMenuModule, MenuEventArgs } from '@syncfusion/ej2-angular-navigations';
import { MenuItemModel } from '@syncfusion/ej2-navigations';
@Component({
selector: 'app-root',
standalone: true,
imports: [ContextMenuModule],
template: `
<div class="e-section-control">
<div id="target">Right click / Touch hold to open the ContextMenu</div>
<ejs-contextmenu
target='#target'
[items]='menuItems'
[enableScrolling]='true'
(beforeOpen)='beforeOpen($event)'>
</ejs-contextmenu>
</div>
`
})
export class AppComponent {
public menuItems: MenuItemModel[] = [
{ text: 'Option 1' },
{ text: 'Option 2' },
{ text: 'Option 3' },
{ text: 'Option 4' },
{ text: 'Option 5' },
{ text: 'Option 6' },
{ text: 'Option 7' },
{ text: 'Option 8' },
{ text: 'Option 9' },
{ text: 'Option 10' }
];
beforeOpen(args: MenuEventArgs): void {
if (args.element && args.element.parentElement) {
// Set max height for scrollable area
args.element.parentElement.style.height = '200px';
}
}
}Setting Menu Container Height
beforeOpen(args: MenuEventArgs): void {
if (args.element && args.element.parentElement) {
args.element.parentElement.style.maxHeight = '300px';
args.element.parentElement.style.overflowY = 'auto';
}
}Responsive Design
Mobile-Friendly Configuration
Adapt menu behavior for different devices:
import { Component } from '@angular/core';
import { ContextMenuModule } from '@syncfusion/ej2-angular-navigations';
import { Browser } from '@syncfusion/ej2-base';
import { MenuItemModel, MenuAnimationSettingsModel } from '@syncfusion/ej2-navigations';
@Component({
selector: 'app-root',
standalone: true,
imports: [ContextMenuModule],
template: `
<div class="e-section-control">
<div id="target">Right click / Touch hold</div>
<ejs-contextmenu
target='#target'
[items]='menuItems'
[animationSettings]='animationSettings'>
</ejs-contextmenu>
</div>
`
})
export class AppComponent {
public menuItems: MenuItemModel[] = [
{ text: 'New' },
{ text: 'Open' },
{ text: 'Save' }
];
public animationSettings: MenuAnimationSettingsModel;
constructor() {
// Adjust animation for mobile devices
if (Browser.isDevice) {
this.animationSettings = {
effect: 'ZoomIn',
duration: 200
};
} else {
this.animationSettings = {
effect: 'SlideDown',
duration: 400
};
}
}
}Responsive Styling
@Component({
selector: 'app-root',
template: `<ejs-contextmenu [items]='menuItems'></ejs-contextmenu>`,
styles: [`
:host ::ng-deep .e-contextmenu-wrapper {
font-size: 14px;
}
/* Tablet screens */
@media (max-width: 768px) {
:host ::ng-deep .e-contextmenu-wrapper ul .e-menu-item {
padding: 12px 16px;
font-size: 16px;
}
}
/* Mobile screens */
@media (max-width: 480px) {
:host ::ng-deep .e-contextmenu-wrapper ul .e-menu-item {
padding: 16px 20px;
font-size: 18px;
}
}
`]
})
export class AppComponent {
public menuItems: MenuItemModel[] = [
{ text: 'New' },
{ text: 'Open' },
{ text: 'Save' }
];
}Complete Example: Advanced Customization
import { Component, ViewChild } from '@angular/core';
import { ContextMenuComponent, ContextMenuModule, MenuEventArgs } from '@syncfusion/ej2-angular-navigations';
import { MenuItemModel, MenuAnimationSettingsModel } from '@syncfusion/ej2-navigations';
import { Browser } from '@syncfusion/ej2-base';
@Component({
selector: 'app-root',
standalone: true,
imports: [ContextMenuModule],
template: `
<div class="e-section-control">
<div id="target">Right click / Touch hold</div>
<ejs-contextmenu
#contextmenu
target='#target'
[items]='menuItems'
[animationSettings]='animationSettings'
[enableScrolling]='true'
(beforeOpen)='beforeOpen($event)'
(select)='onSelect($event)'>
</ejs-contextmenu>
</div>
`,
styles: [`
:host ::ng-deep .e-contextmenu-wrapper {
background: #f8f9fa;
border: 2px solid #dee2e6;
border-radius: 6px;
box-shadow: 0 2px 8px rgba(0,0,0,0.12);
}
:host ::ng-deep .e-contextmenu-wrapper ul .e-menu-item {
color: #212529;
padding: 10px 16px;
transition: all 0.2s ease;
}
:host ::ng-deep .e-contextmenu-wrapper ul .e-menu-item:hover {
background-color: #e7f1ff;
color: #0066cc;
}
:host ::ng-deep .e-contextmenu-wrapper ul .e-menu-item.e-selected {
background-color: #0066cc;
color: #fff;
}
:host ::ng-deep .e-separator {
background-color: #dee2e6;
margin: 5px 0;
}
`]
})
export class AppComponent {
@ViewChild('contextmenu')
public contextmenu?: ContextMenuComponent;
public menuItems: MenuItemModel[] = [
{ text: 'New', iconCss: 'e-icons e-new' },
{ text: 'Open', iconCss: 'e-icons e-open' },
{ text: 'Save', iconCss: 'e-icons e-save' },
{ separator: true },
{ text: 'Print', iconCss: 'e-icons e-print' },
{ text: 'Settings', iconCss: 'e-icons e-settings' }
];
public animationSettings: MenuAnimationSettingsModel = Browser.isDevice
? { effect: 'ZoomIn', duration: 200 }
: { effect: 'SlideDown', duration: 400 };
beforeOpen(args: MenuEventArgs): void {
if (args.element && args.element.parentElement) {
args.element.parentElement.style.maxHeight = '300px';
}
}
onSelect(args: MenuEventArgs): void {
console.log(`Selected: ${args.item.text}`);
}
}---
Next: Create custom templates and advanced features in references/templates-and-advanced.md.