Dynamic nested Material menu from json object in Angular 5
The following structure should work for you:
<button mat-button [matMenuTriggerFor]="main_menu">My menu</button><mat-menu #main_menu="matMenu"> <ng-container *ngFor="let mainItem of objectKeys(my_menu)"> <button mat-menu-item [matMenuTriggerFor]="sub_menu">{{ mainItem }}</button> <mat-menu #sub_menu="matMenu"> <button *ngFor="let subItem of my_menu[mainItem]" mat-menu-item>{{ subItem }}</button> </mat-menu> </ng-container></mat-menu>
Since I placed sub_menu
inside the embedded template (*ngFor
) we can use the same name for template reference variable(#sub_menu
).
Here is a StackBlitz example of an arbitrarily deep nesting based on JSON (authored by @Splaktar)
The key to arbitrary nesting is the self-referencing menu-item.component:
import {Component, Input, OnInit, ViewChild} from '@angular/core';import {Router} from '@angular/router';import {NavItem} from '../nav-item';@Component({ selector: 'app-menu-item', templateUrl: './menu-item.component.html', styleUrls: ['./menu-item.component.scss']})export class MenuItemComponent implements OnInit { @Input() items: NavItem[]; @ViewChild('childMenu') public childMenu; constructor(public router: Router) { } ngOnInit() { }}
<mat-menu #childMenu="matMenu" [overlapTrigger]="false"> <span *ngFor="let child of items"> <!-- Handle branch node menu items --> <span *ngIf="child.children && child.children.length > 0"> <button mat-menu-item color="primary" [matMenuTriggerFor]="menu.childMenu"> <mat-icon>{{child.iconName}}</mat-icon> <span>{{child.displayName}}</span> </button> <app-menu-item #menu [items]="child.children"></app-menu-item> </span> <!-- Handle leaf node menu items --> <span *ngIf="!child.children || child.children.length === 0"> <button mat-menu-item [routerLink]="child.route"> <mat-icon>{{child.iconName}}</mat-icon> <span>{{child.displayName}}</span> </button> </span> </span></mat-menu>