A context menu built with Angular 2 inspired by ui.bootstrap.contextMenu. Bootstrap classes are included in the markup, but there is no explicit dependency on Bootstrap. Demo
Installation
npm install angular2-contextmenu
- import ContextMenuModule into your app module
Usage
Declarative vs. Imperative
With version 0.2.0, there is a new declarative syntax that allows for quite a bit more flexibility and keeps html out of configuration objects.
The older syntax is deprecated and will be removed in version 1.x. (I have no timeline on when I'll release 1.x, but wanted to give everyone advance warning.)
Template
<ul>
<li *ngFor="let item of items" [contextMenu]="basicMenu" [contextMenuSubject]="item">Right Click: {{item?.name}}</li>
</ul>
<context-menu>
<template contextMenuItem (execute)="showMessage('Hi, ' + $event.item.name)">
Say hi!
</template>
<template contextMenuItem let-item (execute)="showMessage($event.item.name + ' said: ' + $event.item.otherProperty)">
Bye, {{item?.name}}
</template>
</context-menu>
Component Code
@Component({
...
})
export class MyContextMenuClass {
public items = [
{ name: 'John', otherProperty: 'Foo' },
{ name: 'Joe', otherProperty: 'Bar' }
};
@ViewChild(ContextMenuComponent) public basicMenu: ContextMenuComponent;
}
- Each context menu item is a
<template>
element with the contextMenuItem
attribute directive applied. - If the
item
object is used in the context menu item template, the let-item
attribute must be applied to the <template>
element.
** Note: ** Make sure to use the item?.property
syntax in the template rather than item.property
as the item will be initially undefined
. - Every context menu item emits
execute
events. The $event
object is of the form { event: MouseEvent, item: any }
where event
is the mouse click event
that triggered the execution and item
is the current item. - The
enabled
input parameter is optional. Items are enabled by default.
This can be a boolean value or a function definition that takes an item and returns a boolean. - The
visible
input parameter is optional. Items are visible by default. This property enables you to show certain context menu items based on what the data item is.
This can be a boolean value or a function definition that takes an item and returns a boolean. - Within the template, you have access to any components and variables available in the outer context.
<context-menu>
<template contextMenuItem let-item [visible]="isMenuItemType1" [enabled]="false" (execute)="showMessage('Hi, ' + $event.item.name)">
Say hi, {{item?.name}}! <my-component [attribute]="item"></my-component>
With access to the outside context: {{ outsideValue }}
</template>
</context-menu>
public outsideValue = "something";
public isMenuItemType1(item: any): boolean {
return item.type === 'type1';
}
You can use multiple context menus in the same component if you would like.
<ul>
<li *ngFor="let item of items" [contextMenu]="basicMenu" [contextMenuSubject]="item">{{item?.name}}</li>
</ul>
<context-menu #basicMenu>
...
</context-menu>
<ul>
<li *ngFor="let item of items" [contextMenu]="otherMenu" [contextMenuSubject]="item">{{item?.name}}</li>
</ul>
<context-menu #otherMenu>
...
</context-menu>
@ViewChild('basicMenu') public basicMenu: ContextMenuComponent;
@ViewChild('otherMenu') public otherMenu: ContextMenuComponent;
If your <context-menu>
component is in a different component from your list, you'll need to wire up the context menu event yourself.
<ul>
<li *ngFor="item in items" (contextmenu)="onContextMenu($event, item)">Right Click: {{item.name}}</li>
</ul>
import { ContextMenuService } from 'angular2-contextmenu';
@Component({
...
})
export class MyContextMenuClass {
public items = [
{ name: 'John', otherProperty: 'Foo' },
{ name: 'Joe', otherProperty: 'Bar' }
};
constructor(private contextMenuService: ContextMenuService) {}
public onContextMenu($event: MouseEvent, item: any): void {
this.contextMenuService.show.next({
event: $event,
item: item,
});
$event.preventDefault();
$event.stopPropagation();
}
}
Custom Styles
The html that is generated for the context menu looks like this:
<div class="dropdown angular2-contextmenu">
<ul class="dropdown-menu">
<li>
<a></a>
</li>
</ul>
</div>
You can key off of the angular2-contextmenu
class to create your own styles. Note that the ul.dropdown-menu
will have inline styles applied for position
, display
, left
and top
so that it will be positioned at the cursor when you right-click.
.angular2-contextmenu .dropdown-menu {
border: solid 1px chartreuse;
background-color: darkgreen;
padding: 0;
}
.angular2-contextmenu li {
display: block;
border-top: solid 1px chartreuse;
text-transform: uppercase;
text-align: center;
}
.angular2-contextmenu li:first-child {
border-top:none;
}
.angular2-contextmenu a {
color:chartreuse;
display: block;
padding: 0.5em 1em;
}
.angular2-contextmenu a:hover {
color:darkgreen;
background-color:chartreuse;
}
Bootstrap 4
If you're using Bootstrap 4, you can add a useBootstrap4
attribute to the context-menu
component in order to get the appropriate class names. Like this:
<context-menu [useBootstrap4]="true"></context-menu>
Close event emitter
There is a (close)
output EventEmitter that you can subscribe to for notifications when the context menu closes (either by clicking outside or choosing a menu item).
<context-menu (close)="processContextMenuCloseEvent()"></context-menu>
Deprecated syntax
This alternate, deprecated syntax will continue working until version 1.x.
Template
<ul>
<li *ngFor="item in items" (contextmenu)="onContextMenu($event, item)">Right Click: {{item.name}}</li>
</ul>
<context-menu></context-menu>
Component Code
import { ContextMenuService } from 'angular2-contextmenu';
@Component({
...
})
export class MyContextMenuClass {
public items = [
{ name: 'John', otherProperty: 'Foo' },
{ name: 'Joe', otherProperty: 'Bar' }
};
constructor(private contextMenuService: ContextMenuService) {}
public onContextMenu($event: MouseEvent, item: any): void {
this.contextMenuService.show.next({
actions: [
{
html: (item) => `Say hi!`,
click: (item) => alert('Hi, ' + item.name)
},
{
html: (item) => `Something else`,
click: (item) => alert('Or not...')
},
],
event: $event,
item: item,
});
$event.preventDefault();
$event.stopPropagation();
}
}