{"id":12837,"date":"2022-10-29T12:06:06","date_gmt":"2022-10-29T12:06:06","guid":{"rendered":"https:\/\/ui-lib.com\/blog\/?p=12837"},"modified":"2024-04-29T18:56:19","modified_gmt":"2024-04-29T18:56:19","slug":"angular-multi-level-menu","status":"publish","type":"post","link":"https:\/\/ui-lib.com\/blog\/angular-multi-level-menu\/","title":{"rendered":"How to Create a Angular multi level Menu"},"content":{"rendered":"\n<p>Multi level or unlimited level menu are commonly used in admin panel. <a href=\"https:\/\/ui-lib.com\/downloads\/matx-free-angular-material-admin-template\/\">MatX Angular<\/a> dashboard has unlimited level menu. MatX is a Free Angular material design admin dashboard template. This side navigation of MatX has unlimited level menu. Let&#8217;s take a look at the data structure of this menu.<\/p>\n\n\n\n<p>File on github: <a href=\"https:\/\/github.com\/uilibrary\/matx-angular\/blob\/master\/src\/app\/shared\/services\/navigation.service.ts\" target=\"_blank\" rel=\"noreferrer noopener\">navigation.service.ts<\/a><\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: jscript; gutter: false; title: ; quick-code: false; notranslate\" title=\"\">\niconMenu: IMenuItem&#x5B;] = &#x5B;{\n      name: 'WIZARD',\n      state: 'forms\/wizard',\n      type: 'link',\n      icon: 'grain',\n    },\n    {\n      name: 'Multi Level',\n      type: 'dropDown',\n      tooltip: 'Multi Level',\n      icon: 'format_align_center',\n      sub: &#x5B;\n        { name: 'Level Two', state: 'fake-4' },\n        {\n          name: 'Level Two',\n          type: 'dropDown',\n          sub: &#x5B;\n            { name: 'Level Three', state: 'fake-2' },\n            { \n              name: 'Level Three', \n              type: 'dropDown',\n              sub: &#x5B;\n                { name: 'Level Four', state: 'fake-3' },\n                { \n                  name: 'Level Four', \n                  type: 'dropDown',\n                  sub: &#x5B;\n                    { name: 'Level Five', state: 'fake-3' },\n                    { name: 'Level Five', type: 'link', state: 'fake-3' }\n                  ]\n                }\n              ]\n            }\n          ]\n        },\n        { name: 'Level Two', state: 'fake-5' }\n      ]\n    }]\n<\/pre><\/div>\n\n\n<p><br>Dropdown menu has <code>type: 'dropDown'<\/code> and a <code>sub: []<\/code> property, sub contains the child items.<br><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-the-logic\">The Logic<\/h2>\n\n\n\n<p>Inside the HTML template we have the logic. Menu items array is coming from <code>items<\/code> props of the component. <\/p>\n\n\n\n<p><meta charset=\"utf-8\">File on github: <a href=\"https:\/\/github.com\/uilibrary\/matx-angular\/blob\/master\/src\/app\/shared\/components\/sidenav\/sidenav.component.ts\" target=\"_blank\" rel=\"noreferrer noopener\">sidenav.component.ts<\/a><\/p>\n\n\n\n<p><code>@Input('items') public menuItems: any[] = [];<\/code><\/p>\n\n\n\n<p><a href=\"https:\/\/github.com\/uilibrary\/matx-angular\/blob\/master\/src\/app\/shared\/components\/sidenav\/sidenav.template.html\" target=\"_blank\" rel=\"noopener\">sidenav.template.html<\/a><\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: xml; auto-links: false; gutter: false; title: ; quick-code: false; notranslate\" title=\"\">\n&lt;div class=&quot;sidenav-hold&quot; #sidenav&gt;\n\n  &lt;ng-container *ngTemplateOutlet=&quot;menuTemplate; context: {menuItems: menuItems}&quot;&gt;&lt;\/ng-container&gt;\n\n&lt;\/div&gt;\n\n&lt;ng-template #menuTemplate let-menuItems=&quot;menuItems&quot;&gt;\n  &lt;ul appDropdown class=&quot;sidenav&quot;&gt;\n    &lt;li *ngFor=&quot;let item of menuItems&quot; appDropdownLink routerLinkActive=&quot;open&quot;&gt;\n      \n\n      &lt;!-- Item --&gt;\n      &lt;!-- MENU ITEM --&gt;\n      &lt;div *ngIf=&quot;!item.disabled &amp;&amp; item.type !== &#039;separator&#039; &amp;&amp; item.type !== &#039;icon&#039;&quot; class=&quot;lvl1&quot;&gt;\n        &lt;a routerLink=&quot;\/{{item.state}}&quot; appDropdownToggle matRipple\n          *ngIf=&quot;item.type !== &#039;extLink&#039; &amp;&amp; item.type !== &#039;dropDown&#039;&quot; routerLinkActive=&quot;open&quot;&gt;\n          &lt;mat-icon *ngIf=&quot;item.icon&quot; class=&quot;sidenav-mat-icon&quot;&gt;{{item.icon}}&lt;\/mat-icon&gt;\n          &lt;mat-icon *ngIf=&quot;item.svgIcon&quot; &#x5B;svgIcon]=&quot;item.svgIcon&quot; class=&quot;svgIcon&quot;&gt;&lt;\/mat-icon&gt;\n          &lt;span class=&quot;item-name lvl1&quot;&gt;{{item.name | translate}}&lt;\/span&gt;\n          &lt;span fxFlex&gt;&lt;\/span&gt;\n          &lt;span class=&quot;menuitem-badge mat-bg-{{ badge.color }}&quot; &#x5B;ngStyle]=&quot;{background: badge.color}&quot;\n            *ngFor=&quot;let badge of item.badges&quot;&gt;{{ badge.value }}&lt;\/span&gt;\n        &lt;\/a&gt;\n\n        &lt;!-- DropDown --&gt;\n        &lt;a *ngIf=&quot;item.type === &#039;dropDown&#039;&quot; appDropdownToggle matRipple&gt;\n          &lt;mat-icon *ngIf=&quot;item.icon&quot; class=&quot;sidenav-mat-icon&quot;&gt;{{item.icon}}&lt;\/mat-icon&gt;\n          &lt;mat-icon *ngIf=&quot;item.svgIcon&quot; &#x5B;svgIcon]=&quot;item.svgIcon&quot; class=&quot;svgIcon&quot;&gt;&lt;\/mat-icon&gt;\n          &lt;span class=&quot;item-name lvl1&quot;&gt;{{item.name | translate}}&lt;\/span&gt;\n          &lt;span fxFlex&gt;&lt;\/span&gt;\n          &lt;span class=&quot;menuitem-badge mat-bg-{{ badge.color }}&quot; &#x5B;ngStyle]=&quot;{background: badge.color}&quot;\n            *ngFor=&quot;let badge of item.badges&quot;&gt;{{ badge.value }}&lt;\/span&gt;\n          &lt;mat-icon class=&quot;menu-caret&quot;&gt;keyboard_arrow_right&lt;\/mat-icon&gt;\n        &lt;\/a&gt;\n\n        &lt;div *ngIf=&quot;item.type === &#039;dropDown&#039;&quot;&gt;\n          &lt;ng-container *ngTemplateOutlet=&quot;menuTemplate; context: {menuItems: item.sub}&quot;&gt;&lt;\/ng-container&gt;\n        &lt;\/div&gt;\n\n      &lt;\/div&gt;\n    &lt;\/li&gt;\n  &lt;\/ul&gt;\n&lt;\/ng-template&gt;\n<\/pre><\/div>\n\n\n<p>We used <code>ng-template<\/code> to render the dropdown menu. We called it <code>#menuTemplate<\/code>. We passed the array of menu items to the <code>ng-template<\/code>.<\/p>\n\n\n\n<p><br>We iterate through the array of menu items and render each item. Then we used <code>*ngIf<\/code> to conditionally render different types of menu items such as <code>link<\/code>, <code>dropdown<\/code>, <code>extLink<\/code>.<br>If the item type is <code>dropDown<\/code> we render the dropdown toggle item first. Then the child menu items directly underneath of it.<\/p>\n\n\n\n<p><br>And here is the tricky part. We called ng-template #menuTemplate inside itself. Means it&#8217;s calling or referencing itself recursively. And we passed the array of submenu or child items. It renders the child items inside itself. <\/p>\n\n\n\n<p>And again if it finds another dropDown during rending the child items it will again call the ng-template inside child items and render grandchild items.This process will go on as long as it finds the child items.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><br><strong><strong>Expansion panel<\/strong><\/strong><\/h2>\n\n\n\n<p>This is how the rendering works. Now let&#8217;s talk about how expansion panel works. We created few directives. <code>appDropdown<\/code> directive which is attached to the <code>ul<\/code> tag of menu.<br><code>appDropdownLink<\/code> which is attached to the <code>li<\/code> tag of an item<br>and the <code>appDropdownToggle<\/code> toggle which is attached to the <code>a<\/code> tag of an item.<br>These three directive does one job. Add and remove open class to the li tag if you click on it.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><br><strong>CSS<\/strong><\/h2>\n\n\n\n<p>We need a little bit of css to make the drop down Menu actually open and close on click. By default the <code>ul<\/code> tag of child menu is set to <code>max-height: 0<\/code>, when the open class added by clicking the its parent item we set <code>max-height<\/code> to a big number like <code>1000px<\/code>. We assumed the height of the child menu will be always less than <code>1000px<\/code>.You can always increase this number. Here is the <a href=\"https:\/\/github.com\/uilibrary\/matx-angular\/blob\/master\/src\/assets\/styles\/scss\/main\/_sidenav.scss#L102\" target=\"_blank\" rel=\"noreferrer noopener\">CSS code example<\/a><\/p>\n\n\n\n<figure class=\"wp-block-embed is-type-wp-embed is-provider-ui-lib wp-block-embed-ui-lib\"><div class=\"wp-block-embed__wrapper\">\n<blockquote class=\"wp-embedded-content\" data-secret=\"PEI8dmMapK\"><a href=\"https:\/\/ui-lib.com\/downloads\/matx-pro-angular-dashboard-template\/\">MatX Pro &#8211; Angular Admin Dashboard Template<\/a><\/blockquote><iframe loading=\"lazy\" class=\"wp-embedded-content\" sandbox=\"allow-scripts\" security=\"restricted\" style=\"position: absolute; clip: rect(1px, 1px, 1px, 1px);\" title=\"&#8220;MatX Pro &#8211; Angular Admin Dashboard Template&#8221; &#8212; Ui Lib\" src=\"https:\/\/ui-lib.com\/downloads\/matx-pro-angular-dashboard-template\/embed\/#?secret=XqzOQNGzrR#?secret=PEI8dmMapK\" data-secret=\"PEI8dmMapK\" width=\"500\" height=\"282\" frameborder=\"0\" marginwidth=\"0\" marginheight=\"0\" scrolling=\"no\"><\/iframe>\n<\/div><\/figure>\n\n\n\n<figure class=\"wp-block-embed is-type-wp-embed is-provider-ui-lib wp-block-embed-ui-lib\"><div class=\"wp-block-embed__wrapper\">\n<blockquote class=\"wp-embedded-content\" data-secret=\"5ylyVXswr8\"><a href=\"https:\/\/ui-lib.com\/downloads\/matx-free-angular-material-admin-template\/\">MatX &#8211; Free Angular Dashboard Template<\/a><\/blockquote><iframe loading=\"lazy\" class=\"wp-embedded-content\" sandbox=\"allow-scripts\" security=\"restricted\" style=\"position: absolute; clip: rect(1px, 1px, 1px, 1px);\" title=\"&#8220;MatX &#8211; Free Angular Dashboard Template&#8221; &#8212; Ui Lib\" src=\"https:\/\/ui-lib.com\/downloads\/matx-free-angular-material-admin-template\/embed\/#?secret=UV2imoGytG#?secret=5ylyVXswr8\" data-secret=\"5ylyVXswr8\" width=\"500\" height=\"282\" frameborder=\"0\" marginwidth=\"0\" marginheight=\"0\" scrolling=\"no\"><\/iframe>\n<\/div><\/figure>\n","protected":false},"excerpt":{"rendered":"<p>Multi level or unlimited level menu are commonly used in admin panel. MatX Angular dashboard has unlimited level menu. MatX is a Free Angular material&#8230;<\/p>\n<div class=\"more-link-wrapper\"><a class=\"more-link\" href=\"https:\/\/ui-lib.com\/blog\/angular-multi-level-menu\/\">Continue reading<span class=\"screen-reader-text\">How to Create a Angular multi level Menu<\/span><\/a><\/div>\n","protected":false},"author":1,"featured_media":12853,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-12837","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","entry"],"_links":{"self":[{"href":"https:\/\/ui-lib.com\/blog\/wp-json\/wp\/v2\/posts\/12837","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ui-lib.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/ui-lib.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/ui-lib.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/ui-lib.com\/blog\/wp-json\/wp\/v2\/comments?post=12837"}],"version-history":[{"count":19,"href":"https:\/\/ui-lib.com\/blog\/wp-json\/wp\/v2\/posts\/12837\/revisions"}],"predecessor-version":[{"id":12980,"href":"https:\/\/ui-lib.com\/blog\/wp-json\/wp\/v2\/posts\/12837\/revisions\/12980"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/ui-lib.com\/blog\/wp-json\/wp\/v2\/media\/12853"}],"wp:attachment":[{"href":"https:\/\/ui-lib.com\/blog\/wp-json\/wp\/v2\/media?parent=12837"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ui-lib.com\/blog\/wp-json\/wp\/v2\/categories?post=12837"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ui-lib.com\/blog\/wp-json\/wp\/v2\/tags?post=12837"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}