Issue
I want to make a dynamic menu that come from my API so I can add/remove category from a "dashboard" in my app.
For the moment I have a hardcoded HTML as below
<button mat-button class="extras" (click)="toggleDropdown('clothing')">CLOTHINGS <span class="additional">+</span></button>
<div [ngClass]="{'show' : shows.clothing}" class="dropdown clothing">
<button mat-menu-item routerLink="category/clothing">All</button>
<button mat-menu-item>Corsets</button>
<button mat-menu-item class="extras" (click)="toggleDropdown('dresses')">Dresses<span class="additional">+</span></button>
<div [ngClass]="{'show' : shows.dresses}" class="dropdown dresses">
<button mat-menu-item> Maxi Dresses</button>
<button mat-menu-item> Midi Dresses</button>
<button mat-menu-item> Mini Dresses</button>
</div>
<button mat-menu-item>Denim</button>
<button mat-menu-item>Tops</button>
<button mat-menu-item class="extras" (click)="toggleDropdown('bottoms')">Bottoms<span class="additional">+</span></button>
<div [ngClass]="{'show' : shows.bottoms}" class="dropdown bottoms">
<button mat-menu-item>Trousers</button>
<button mat-menu-item>Skirts</button>
<button mat-menu-item>Shorts</button>
<button mat-menu-item>Playsuits</button>
</div>
<button mat-menu-item>Loungewear</button>
<button mat-menu-item>Outerwear</button>
<button mat-menu-item>Sweats</button>
</div>
<button mat-button class="extras" (click)="toggleDropdown('collections')">BY COLLECTIONS <span class="additional">+</span></button>
<div [ngClass]="{'show' : shows.collections}" class="dropdown collections">
<button mat-menu-item routerLink="category/collections">All</button>
<button mat-menu-item>Summer Collection</button>
<button mat-menu-item>Winter Collection</button>
<button mat-menu-item>Spring Collection</button>
<button mat-menu-item>Fall Collection</button>
</div>
<button mat-button routerLink="accessories">ACCESSORIES</button>
I created a JSON for the menu so I would display it instead of this hard coded html
export const menu = [
{
name: "clothings",
children: [
{ name: "all", path: "clothing/all"},
{ name: "corsets", path: "clothing/corsets"},
{ name: "dresses", children: [
{ name:"maxi dresses", path: "clothing/dresses/maxi-dresses"},
{ name:"midi dresses", path: "clothing/dresses/midi-dresses"},
{ name:"mini dresses", path: "clothing/dresses/mini-dresses"}
]},
{ name: "denim", path: "clothing/denim"},
{ name: "tops", path: "clothing/tops"},
{ name: "bottoms", children: [
{ name:"trousers", path: "clothing/bottoms/trousers"},
{ name:"skirts", path: "clothing/bottoms/skirts"},
{ name:"shorts", path: "clothing/bottoms/shorts"},
{ name:"playsuits", path: "clothing/bottoms/playsuits"},
]},
{ name: "loungewear", path: "clothing/loungewear"},
{ name: "outerwear", path: "clothing/outerwear"},
{ name: "sweats", path: "clothing/sweats"}
],
},
{
name: "by collection",
children: [
{ name: "all", path: "collection/all"},
{ name: "summer collection", path: "collection/summer-collection"},
{ name: "winter collection", path: "collection/winter-collection"},
{ name: "spring collection", path: "collection/spring-collection"},
{ name: "fall collection", path: "collection/fall-collection"}
]
},
{
name: "accessories",
children: [
{ name: "all", path: "accessories/all"},
{ name: "glasses", path: "accessories/glasses"},
{ name: "necklace", path: "accessories/necklace"},
]
},
]
I tried this for the moment
<div *ngFor="let category of menu">
<button mat-button class="extras" (click)="toggleDropdown(category.name)">{{category.name}}<span class="additional">+</span></button>
<div *ngFor="let child of category.children">
<div [ngClass]="{'show': shows[category.name]}" class="dropdown {{category.name}}">
<button mat-menu-item (click)="child.children && toggleDropdown(child.name)">{{child.name}}</button>
<div *ngFor="let subchild of child.children">
<div [ngClass]="{'show' : shows[subchild.name]}" class="dropdown {{subchild.name}}">
<button mat-menu-item>{{subchild.name}}</button>
</div>
</div>
</div>
</div>
</div>
This displays correctly the parents category and their childs ("clothing", "by collection" and "accessories" display its children based on the ngClass
But the subchildren of their childre doesn’t work.
I wonder if I am using the correct way to go because it’s seems to me a bit confusing all of these *ngFor
Solution
Try using the ‘matMenuTriggerFor’ attribute of the mat-menu, for your nested menu options, instead of using a nested for loop.
<mat-menu #myMenu="matMenu">
<!-- menu option without sub menus -->
<button mat-menu-item (click)='someFunction()'>Call function</button>
<!-- menu option with sub menus -->
<button mat-menu-item [matMenuTriggerFor]="subMenu">Show sub menu</button>
</mat-menu>
<mat-menu #subMenu="matMenu">
<button mat-menu-item>subOpt1</button>
<button mat-menu-item>subOpt2</button>
</mat-menu>
So because you have nested dynamic menus, you can render each of the dynamic sub-menus separately using different tags and ngFor loops. Finally, you can link a sub menu to a main menu, by using the ‘matMenuTriggerFor’ attribute
You may view more examples at the below link :
https://material.angular.io/components/menu/overview#menu-nested