@fullerstack/ngx-menu
A simple menu library for Angular applications
Overview
Description
Creating a menu system can be a complicated task, and to avoid hard-coding repeated logic, this library creates complex nested menu system, derived from data tables.
@fullerstack/ngx-menu attempts to streamline the menu system of your application, while promoting DRY DRY.
How to install
npm i @fullerstack/ngx-menu |OR| yarn add @fullerstack/ngx-menu
How to use
// app.module.ts
import { MenuModule, MenuService } from '@fullerstack/ngx-menu';
NgModule({
declarations: [AppComponent],
imports: [BrowserModule, MenuModule],
provides: [MenuService],
bootstrap: [AppComponent],
});
export class AppModule {}
// app.component.ts
import { Component } from '@angular/core';
import { MenuService, MenuItem } from '@fullerstack/ngx-menu';
export const AppMenuTree: MenuItem[] = [
{
name: 'Admin',
icon: 'wrench',
permissions: [
// superuser - full authorization
'admin_superuser',
// staff - limited auth - (ex: can't delete user)
'admin_staff',
// finance - limited auth - (ex: subscriptions, payable, etc.)
'admin_finance',
// HR = limited auth - (ex: access to performance reviews, benefits, etc.)
'admin_hr',
],
children: [
{
name: 'Accounts',
icon: 'account',
link: '/admin/accounts/profile',
},
{
name: 'Settings',
icon: 'account-cog',
link: '/admin/accounts/settings',
fullspan: true,
permissions: ['admin_root', 'admin_staff', 'admin_hr'],
},
{
name: 'Subscriptions',
icon: ' account-multiple-check',
link: '/admin/accounts/Subscriptions',
fullspan: true,
permissions: ['admin_root', 'admin_staff', 'admin_finance'],
},
],
},
{
name: 'Stocks',
icon: 'trending-up',
children: [
{
name: 'Sandbox Portfolio', // all users can simulate buy/sell stocks
icon: 'account-check',
link: '/finance/stocks/own',
},
{
name: 'Wishlist',
icon: 'playlist-check',
link: '/finance/stocks/wishlist',
disabled: true, // feature disabled (feature not ready)
},
{
name: 'Portfolio', // paid users have real portfolio
icon: 'account-check',
link: '/finance/stocks/own',
permissions: ['subscriptions_level_1'],
},
{
name: 'Trade', // paid users can trade
icon: ' home-currency-usd',
link: '/finance/stocks/trade',
permissions: ['subscriptions_level_1'],
},
],
},
{
name: 'Yahoo Finance',
icon: 'google-analytics',
link: 'https://yahoo.com',
external: true,
},
{
name: 'Youtube',
icon: 'youtube',
link: 'https://youtube.com',
external: true,
target: '_blank',
},
];
@Component({
selector: 'fullerstack-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent {
title = 'Fullerstack';
menuTree = null;
user: {
// user is fetched from the server upon authentication
firstName: 'Mike';
lastName: 'Tyson';
permissions: ['subscriptions_level_1'];
};
constructor(public menu: MenuService) {
this.log.info('AppComponent loaded ...');
this.buildMenu();
}
/**
* Given a MenuItem, it returns true if item is allowed to display
* @param node - MenuItem
*/
private hasPermission(node: MenuItem) {
const menuPerms = node.permissions || [];
if (menuPerms.length === 0) {
return true;
}
if (!this.user) {
return false;
}
const userPerms = this.user.permissions || [];
if (menuPerms.length === 0) {
return false;
}
const hasPerm = menuPerms.some((value) => userPerms.indexOf(value) >= 0);
return hasPerm;
}
buildMenu() {
this.menu.PermissionVerificationFuncType(this.hasPermission.bind(this));
this.menuTree = this.menu.buildMenuTree(AppMenuTree);
// menu is ready for rendering
}
}
Advanced usage:
The following is the list of available options for each menu item.
export interface MenuItem {
// translatable name of this menu
name: string;
// icon for the menu
icon?: string;
// if link (clickable)
link?: string;
// hides menu, navigation, popups (ex: calendar page is fullspan)
fullspan?: boolean;
// if true, this will be a fullscreen page
fullscreen?: boolean;
// if true, this will be a fullscreen page without header and footer
headless?: boolean;
// if external link
external?: boolean;
// open in new tab (_blank)
target?: string;
// if menu is disabled
disabled?: boolean;
// if menu is allowed (permission) - for manual override
allowed?: boolean;
// permissions required to render this menu
permissions?: string[];
// submenu
children?: MenuItem[];
}
Here are the helper functions on each MenuNode.
// when looping through menu nodes
for (const node in menuTree.children) {
// node.isLink - is a link or a node with children?
// node..isNode - is a node with children?
// node..hasChildren - has children?
// node..isInternalLink - is a link & internal (ex: /dashboard/)
// node..isExternalLink - is a link * external (ex: www.youtube.com)
// node..isFullSpan - is a link that requires menu(s) to slide away
// node..isFullScreen - is a link that requires the page be in fullscreen mode
// node..isHeadless - is a link that requires header/footer to hide away
// node..offset(value: number, unit = 'px') - offset for multi-level menu (for margin or padding)
// node..isActive(url: string) - is this node active for the active route
}
License
Released under a (MIT) license.
Version
X.Y.Z Version
`MAJOR` version -- making incompatible API changes
`MINOR` version -- adding functionality in a backwards-compatible manner
`PATCH` version -- making backwards-compatible bug fixes