Follow the following steps to create Dynamic Menu in Angular v17 with Angular Material v17 with auto hide last open menu.
1. Install Angular Cli v17.3.0
npm i @angular/cli@17.3.0
2. Create new angular project dynamic_menu
ng new dynamic_menu --no-standalone
3. Open the created project folder
cd dynamic_menu
4. Add Angular Material v17.3.7
ng add @angular/material@17.3.7
5. Generate Categories Menu component
ng g c categories-menu
6. Create new file categoriesMenu.ts with the below code:
export class CategoriesMenu {
name: string = '';
children: CategoriesMenu[] = [];
}
7. Open categories-menu.component.ts file and add the below code:
import { Component, Input, Output, EventEmitter } from '@angular/core';
import { MatMenuTrigger } from '@angular/material/menu';
import { CategoriesMenu } from '../categoriesMenu';
@Component({
selector: 'app-categories-menu',
templateUrl: './categories-menu.component.html',
styleUrl: './categories-menu.component.scss'
})
export class CategoriesMenuComponent {
@Input('menuItem')
menuItem!: CategoriesMenu;
@Output()
menuTriggerEvent = new EventEmitter<MatMenuTrigger>();
subMenuTrigger!: MatMenuTrigger;
constructor() { }
handleMenuTrigger(trigger: MatMenuTrigger) {
trigger.openMenu();
this.menuTriggerEvent.emit(trigger);
}
handleSubMenuTrigger(trigger: MatMenuTrigger) {
if (this.subMenuTrigger && this.subMenuTrigger !== trigger) {
this.subMenuTrigger.closeMenu();
}
if (this.subMenuTrigger !== trigger) {
trigger.focus();
trigger.openMenu();
}
this.subMenuTrigger = trigger;
}
}
8. Open categories-menu.component.html file and add the below html:
<ng-container *ngIf="menuItem.children && menuItem.children.length > 0">
<a mat-button mat-menu-item class="mainContainerColor" [matMenuTriggerFor]="subMenu"
#subMenuTrigger="matMenuTrigger" (mouseover)="handleMenuTrigger(subMenuTrigger);">{{menuItem.name}}</a>
<mat-menu #subMenu="matMenu">
<ng-container>
<app-categories-menu [menuItem]="item" (menuTriggerEvent)="handleSubMenuTrigger($event)"
*ngFor="let item of menuItem.children"></app-categories-menu>
</ng-container>
</mat-menu>
</ng-container>
<ng-container *ngIf="!(menuItem.children && menuItem.children.length > 0)">
<a mat-button mat-menu-item class="mainContainerColor">{{menuItem.name}}</a>
</ng-container>
9. Open app.module.ts file and add the below code:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { MatInputModule } from '@angular/material/input';
import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { CategoriesMenuComponent } from './categories-menu/categories-menu.component';
@NgModule({
declarations: [
AppComponent,
CategoriesMenuComponent
],
imports: [
MatInputModule,
MatIconModule,
MatMenuModule,
BrowserAnimationsModule,
BrowserModule,
AppRoutingModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
10. Open app.component.ts and add the below code:
import { Component } from '@angular/core';
import { MatMenuTrigger } from '@angular/material/menu';
import { CategoriesMenu } from './categoriesMenu';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrl: './app.component.scss'
})
export class AppComponent {
title = 'dynamic_menu';
subMenuTrigger!: MatMenuTrigger;
categoriesMenu: CategoriesMenu = {
name: 'menu item 1',
children: [{
name: 'child menu 1',
children: [{
name: 'grandchild menu 1',
children: []
}, {
name: 'grandchild menu 2',
children: []
}, {
name: 'grandchild menu 3',
children: []
}]
}, {
name: 'child menu 2',
children: [{
name: 'grandchild menu 4',
children: []
}, {
name: 'grandchild menu 5',
children: []
}, {
name: 'grandchild menu 6',
children: []
}]
}]
};
handleSubMenuTrigger(trigger: MatMenuTrigger) {
if (this.subMenuTrigger && this.subMenuTrigger !== trigger) {
this.subMenuTrigger.closeMenu();
}
if (this.subMenuTrigger !== trigger) {
trigger.focus();
trigger.openMenu();
}
this.subMenuTrigger = trigger;
}
}
11. Open app.component.html file and add the below code:
<div>
<mat-label>Choose</mat-label>
</div>
<div *ngIf="categoriesMenu">
<button mat-button [matMenuTriggerFor]="menu" #menuTrigger="matMenuTrigger"
style="cursor: pointer;">
<mat-label [ngClass]="['buttonLabel']">{{categoriesMenu.name}}</mat-label>
<mat-icon [ngClass]="['optionButtonIcon']">keyboard_arrow_down</mat-icon>
</button>
<mat-menu #menu="matMenu">
<ng-container>
<app-categories-menu [menuItem]="item"
(menuTriggerEvent)="handleSubMenuTrigger($event)"
*ngFor="let item of categoriesMenu.children"></app-categories-menu>
</ng-container>
</mat-menu>
</div>
12. Run the app:
npm run start
The output will look like below: