Angular 7 - Implement keyboard shortcuts for primeNg Menubar

1k Views Asked by At

I am working on Angular 7 application, it has primeNg menuBar, i want to implement shortcuts for top level menu items(which should open the second level menu items list) and second level menu items(which should directly go to the routerlink provided for that menu item).

My approach:

  1. For menus, i have JSON files to define different menuItems. In this Json file, i have added one new property "shortKey"- it defines which shortkey to be used for that menu Item.
  2. using the above json, primeNg menuItems array is created. Here i am checking IF the menuItem is having a shortnKey available THEN bind the keyBoardEvent for that short key using Angulr2-Hotkeys.
  3. In the callback of keryboard event, for topLevel menu:
    • Finding the top level menu item using querySelector and changing the css style for that dom element to show the menu.
    • Navigating to the component as per routerLink of that menuItem.

Problems I am facing with above approach:

  1. After showing the top level menu, i need to hide it on click event anywhere else in the document. So a listener on document will be listening all the time.
  2. Too much direct dom interaction and dom manipulation.

Component having PrimeNg Menu

import { Component, OnInit, Renderer2 } from '@angular/core';
import { MenuItem } from 'primeng/api';
import { HotkeysService, Hotkey } from 'angular2-hotkeys';
import { NavItem } from '../NavItem';
import { MenuItemContent } from 'primeng/menu';
import { BindHotKeyService } from '../bind-hot-key.service';


@Component({
  selector: 'app-ng-prime-menu',
  templateUrl: './ng-prime-menu.component.html',
  styleUrls: ['./ng-prime-menu.component.scss']
})
export class NgPrimeMenuComponent implements OnInit {
  private items: MenuItem[] = [];
  bindHotKeyService: BindHotKeyService;

  constructor(bindHotKeyService: BindHotKeyService) {
    this.bindHotKeyService = bindHotKeyService;
  }
  ngOnInit() {
    let navItems: NavItem[] = this.getNavItems();
    navItems.forEach(element => {
      let m1: MenuItem = {};
      let mItemsInside: MenuItem[] = [];
      m1.label = element.label;
      m1.id="id_"+m1.label;
      element.shortKey !== ''? this.bindHotKeyService.bindHotKey("menuNavigation",element.shortKey, element.label):"";
      element.navItems.forEach(insideEl => {
        let m1Inside: MenuItem = {};
        m1Inside.label = insideEl.label;
        insideEl.shortKey !== ''? this.bindHotKeyService.bindHotKey("menuItemClick",insideEl.shortKey, insideEl.label):"";
        mItemsInside.push(m1Inside);
      });
      m1.items = mItemsInside;
      this.items.push(m1);
    });

  }
}

Service to Bind Key Board Events(Just a draft)

import { Injectable, Renderer2, RendererFactory2 } from '@angular/core';
import { HotkeysService, Hotkey } from 'angular2-hotkeys';

@Injectable({
  providedIn: 'root'
})
export class BindHotKeyService {
  private renderer: Renderer2;

  constructor(private hkservice: HotkeysService, rendererFactory: RendererFactory2) {
    this.renderer = rendererFactory.createRenderer(null, null);
   }
  bindHotKey(type: string, key: string, menuItemName: string): void {
    if (type === 'menuNavigation') {
      let menuItemId: string =  "#id_"+menuItemName;
      this.hkservice.add(new Hotkey(key, (event: KeyboardEvent): boolean => {
        let menuBar:Element = document.querySelector("p-menubar");
        let menuItemChildNode:Element = menuBar.querySelector(menuItemId);
        menuItemChildNode.parentElement.className = menuItemChildNode.parentElement.className+" ui-menuitem-active";
        menuItemChildNode.parentElement.querySelector("ul").style.zIndex = 
        (Number(menuItemChildNode.parentElement.querySelector("ul").style.zIndex) + 2).toString();
        console.log("Binding HotKey: '" + key + "' to MenuItem: " + menuItemName);
        return false;
      }));
    }
  }
}

**component HTML**
<p-menubar [model]="items" [autoDisplay]="false"> 
</p-menubar>

My questions: 1. Is the above approach correct for binding keys with PrimeNg Menu in Angular 7? If no, What could be a better solution? 2. If approach is ok, how can i mitigate the problems above?

0

There are 0 best solutions below