Angular Dynamic Context Menu
Piyali Das
Looking for QUALITY Works | 10+ yrs | Angular | Micro Frontend | Graphql | RXJS | TypeScript | JavaScript | SASS | Git | Gulp
"contextmenu" is an event like click, mouseup, mousemove, mousedown. This "contextmenu" event is typically triggered by clicking the right mouse button. By default, clicking right mouse button, you will get cut, copy, pase, inspect menus. In my program, i have created a custom context menu.
Let's start my dynamic context menu, it is dynamic because for different sections like List elements and Table, we have different context menu.
For List Elements, onListClick($event) event is called using contextmenu event. contextmenu is an event like onclick, onfocus events and is triggered by clicking right button.
For Table, onTableClick($event) event is called when contextmenu triggered.
app.component.html
<div class="container"> <div class="row"> <div class="col"> <h4>List Elements</h4> <ul class="list"> <li (contextmenu)="onListClick($event)">List Element 1</li> <li (contextmenu)="onListClick($event)">List Element 2</li> <li (contextmenu)="onListClick($event)">List Element 3</li> <li (contextmenu)="onListClick($event)">List Element 4</li> </ul> </div> <div class="col"> <table class="table" (contextmenu)="onTableClick($event)"> <thead> <tr> <th>Firstname</th> <th>Lastname</th> <th>Email</th> </tr> </thead> <tbody> <tr> <td>John</td> <td>Doe</td> <td>[email protected]</td> </tr> <tr> <td>Mary</td> <td>Moe</td> <td>[email protected]</td> </tr> <tr> <td>July</td> <td>Dooley</td> <td>[email protected]</td> </tr> </tbody> </table> </div> </div> <div #contextMenu></div> </div>
<div #contextMenu></div> is needed to display contextmenu. Using Viewchild #contextMenu, we are going to create dynamic context menu inside the div.
app.component.ts
import { Component, OnInit, ComponentFactoryResolver, ViewChild, ViewContainerRef } from '@angular/core'; import { ContextmenuComponent } from './contextmenu/contextmenu.component'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit { title = 'contextmeu'; rightClickMenuItems = []; parentElem: any; contextMenuSelector: string; menuEvent: any; @ViewChild('contextMenu', { read: ViewContainerRef, static: true }) container; constructor(private componentFactoryResolver: ComponentFactoryResolver) { } ngOnInit(): void { } onListClick(event) { console.log(event); this.menuEvent = event; this.contextMenuSelector = event.srcElement; this.rightClickMenuItems = [ { menuText: 'Edit', menuLink: '', }, { menuText: 'Delete', menuLink: '', }, ]; this.createContextMenuComponent(); } onTableClick(event) { console.log(event); this.menuEvent = event; this.contextMenuSelector = event.srcElement; this.rightClickMenuItems = [ { menuText: 'Cut', menuLink: '', }, { menuText: 'Copy', menuLink: '', }, { menuText: 'Paste', menuLink: '', }, ]; this.createContextMenuComponent(); } createContextMenuComponent() { this.container.clear(); const componentFactory = this.componentFactoryResolver.resolveComponentFactory(ContextmenuComponent); const componentRef = this.container.createComponent(componentFactory); (<ContextmenuComponent>componentRef.instance).contextMenuEvent = this.menuEvent; (<ContextmenuComponent>componentRef.instance).contextMenuSelector = this.contextMenuSelector; (<ContextmenuComponent>componentRef.instance).contextMenuItems = this.rightClickMenuItems; } }
Here i am using ComponentFactoryResolver and createComponent to dynamically create the component inside DOM. Here three component instances (contextMenuEvent, contextMenuSelector, contextMenuItems) are sent to contextmenu component as @Input parameters.
contextmenu.component.html
<div class="contextMenu" tabindex="-1" aria-hidden="true"> <ul *ngIf="isDisplayContextMenu"> <li *ngFor="let menuItem of contextMenuItems; index as i" (click)="onContextMenuClick($event, menuItem.menuLink)">{{ menuItem.menuText }}</li> </ul>
</div>
contextmenu.component.ts
import { Component, OnInit, Input, ElementRef, HostListener } from '@angular/core'; @Component({ selector: 'app-contextmenu', templateUrl: './contextmenu.component.html', styleUrls: ['./contextmenu.component.css'] }) export class ContextmenuComponent implements OnInit { @Input() contextMenuEvent; @Input() contextMenuSelector; @Input() contextMenuItems; isDisplayContextMenu: boolean = false; _currentMenuVisible = null; constructor(private elementRef: ElementRef) { this.isDisplayContextMenu = false; } ngOnInit() { this.initContextMenu(); } initContextMenu() { console.log(this.contextMenuSelector); console.log(this.contextMenuEvent); if (this.contextMenuSelector && this.contextMenuEvent) { this.contextMenuEvent.preventDefault(); this.contextMenuEvent.stopPropagation(); this.createContextMenu(this.contextMenuEvent.clientX, this.contextMenuEvent.clientY); this.contextMenuSelector.addEventListener('click', e => { this.closeCurrentlyOpenedMenu(); }); } } createContextMenu(x, y) { this.closeCurrentlyOpenedMenu(); this.isDisplayContextMenu = true; if (this.isDisplayContextMenu && this.elementRef.nativeElement) { console.log(this.elementRef.nativeElement); const contextMenuDiv = this.elementRef.nativeElement.querySelector('.contextMenu'); console.log(contextMenuDiv); if (contextMenuDiv) { this._currentMenuVisible = contextMenuDiv; contextMenuDiv.style.left = x + "px"; contextMenuDiv.style.top = y + "px"; } } } closeContextMenu(menu) { console.log(menu); menu.style.left='0px'; menu.style.top='0px'; this._currentMenuVisible = null; } closeCurrentlyOpenedMenu() { console.log(this._currentMenuVisible); if (this._currentMenuVisible !== null) { this.closeContextMenu(this._currentMenuVisible); } } /* close context menu on left click */ @HostListener('document:click') documentClick(): void { this.isDisplayContextMenu = false; } /* close context menu on "ESC" key keypress */ @HostListener('window:onkeyup') escKeyClick(): void { this.isDisplayContextMenu = false; } }
contextMenuEvent, contextMenuSelector, contextMenuItems are @Input parameters.
contextMenuEvent - event for getting X and Y axis value of Selected element
contextMenuSelector - surceElement value to attach click event listener
contextMenuItems - pass content menu json
Source Url :
Live Url :
Lead UI and Frontend Engineer | Master of Computer Applications - MCA at Swami Vivekananda Subharti University, Meerut
3 年can you share about ag-grid in angular
Lead UI and Frontend Engineer | Master of Computer Applications - MCA at Swami Vivekananda Subharti University, Meerut
3 年https://youtu.be/DsV_MZ5H8Ic