Angular 5 Material Multiple mat-menu

I am quite new to Angular 5 and have just started learning it.

Recently, I have been trying to create a menu bar with multiple menus for my app using Angular 5 Material.
The menu will be triggered/opened during mouse enter and closed when the mouse leaves the menu.
My problem is that everytime the mouse mouse hovers to the first menu it loads the menu items of the 2nd menu.

Here is a screenshot of the problem:
enter image description here

Here are my codes:
mainmenu.component.html:

<div>
    <button mat-button [matMenuTriggerFor]="menu1" 
      (mouseenter)="openMyMenu()">Trigger1</button>
    <mat-menu #menu1="matMenu" overlapTrigger="false">
        <span (mouseleave)="closeMyMenu()">
            <button mat-menu-item>Item 1</button>
            <button mat-menu-item>Item 2</button>
        </span>
   </mat-menu>
</div>
<div>
    <button mat-button [matMenuTriggerFor]="menu2"
      (mouseenter)="openMyMenu()">Trigger2</button>
    <mat-menu #menu2="matMenu" overlapTrigger="false">
        <span (mouseleave)="closeMyMenu()">
            <button mat-menu-item>Item 3</button>
            <button mat-menu-item>Item 4</button>
        </span>
    </mat-menu>
</div>

mainmenu.component.ts:

import { Component, OnInit, ViewChild } from '@angular/core';
import {MatMenuTrigger} from '@angular/material'

@Component({
  selector: 'app-mainmenu',
  templateUrl: './mainmenu.component.html',
  styleUrls: ['./mainmenu.component.css']
})
export class MainmenuComponent implements OnInit {
  @ViewChild(MatMenuTrigger) matMenuTrigger: MatMenuTrigger;

  constructor() { }

  ngOnInit() {
  }
  openMyMenu() {
    this.matMenuTrigger.openMenu();

  } 
  closeMyMenu() {
    this.matMenuTrigger.closeMenu();
  }
}

I also tried this: @ViewChild('menu1') matMenuTrigger: MatMenuTrigger;
but I am getting errors.

Your opinions and advices are very much appreciated!

Thanks,
Artanis Zeratul

References:

10 thoughts on “Angular 5 Material Multiple mat-menu”

  1. I had the same issue.

    Create two separate components, each will then contain its own mat-menu and will not affect the other.

    <!-- component1 -->
    <div>
     <button mat-button [matMenuTriggerFor]="menu1" 
      (mouseenter)="openMyMenu()">Trigger1</button>
     <mat-menu #menu1="matMenu" overlapTrigger="false">
        <span (mouseleave)="closeMyMenu()">
            <button mat-menu-item>Item 1</button>
            <button mat-menu-item>Item 2</button>
        </span>
    </mat-menu>
    </div>
    
    <!-- component2 -->
    <div>
    <button mat-button [matMenuTriggerFor]="menu2"
      (mouseenter)="openMyMenu()">Trigger2</button>
    <mat-menu #menu2="matMenu" overlapTrigger="false">
        <span (mouseleave)="closeMyMenu()">
            <button mat-menu-item>Item 3</button>
            <button mat-menu-item>Item 4</button>
        </span>
     </mat-menu>
    </div>
    
    Reply
  2. The correct solution for this problem:

    @ViewChildren(MatMenuTrigger) trigger: QueryList<MatMenuTrigger>;
    
    //And call:
    
    me.trigger.toArray()[indexOfMenu].openMenu();
    
    Reply
  3. I have two matmenus in my toolbar each one is a separate component and triggers a separate matmenu.

    See images below:

    enter image description here

    Here is my notifications component(component 1 in the image above)
    In my editor view :

    enter image description here

    In my notifications.component.html file :

    <button mat-icon-button [matMenuTriggerFor]="notificationsMenu" (mouseover)="openNotifications()">
      <mat-icon class="material-icons ele-text-color-grey">notifications</mat-icon>
    </button>
    
    <mat-menu #notificationsMenu="matMenu" [overlapTrigger]="false"></mat-menu>
    

    I don’t think it is possible to have two in one component but I hope this helps.

    Reply
  4. This issue is related to the element referencing in angular, so you cannot directly use the mat-menu in a single component.

    The trick to do is to create a component that implements the mat-menu:
    Eg,

    mat-menu.component.html:

    `<div>
        <span>
            <a (click)="openSelectMenu()">Menu
            <mat-icon>arrow_drop_down</mat-icon>
            <div #menuTrigger="matMenuTrigger" [matMenuTriggerFor]="menu1"></div>
            </a>
            <mat-menu #menu1="matMenu" overlapTrigger="false">
            <a mat-menu-item *ngFor="let menu of menuItems">{{menu}}</a></mat-menu>
        </span>
    </div>`
    

    mat-menu.component.ts

    `@ViewChild(MatMenuTrigger) trigger: MatMenuTrigger;`
    `menuItems=['1', '2', '3'];`
    
     `openSelectMenu() {
        this.trigger.openMenu();
     }`
    

    Now you can use this component multiple times in any component.
    Eg,
    app.component.html

    `<app-menu></app-menu>
    <app-menu></app-menu>`
    

    It will work.

    Reply
  5. I had the same issue and I solved it by using the read metadata property of @ViewChild decorator

    mainmenu.component.html

    <button #menuBtn1 [matMenuTriggerFor]="menu1">Trigger1</button>
    <button #menuBtn2 [matMenuTriggerFor]="menu2">Trigger2</button>
    

    mainmenu.component.ts

    @ViewChild('menuBtn1', { read: MatMenuTrigger, static: false}) menu1: MatMenuTrigger;
    @ViewChild('menuBtn2', { read: MatMenuTrigger, static: false}) menu2: MatMenuTrigger;
    
    foo() {
        this.menu1.openMenu(); // also closeMenu()
        this.menu2.openMenu();
    }
    

    The trick is to use the template reference menuBtn1 or menuBtn2 and specify through the read property what you want to get that is the MatMenuTrigger directive

    NOTE: I saw that the question refers to angular and angular-material 5. I tested it with angular 8 but it should be the same

    Reply
  6. <ul class="navbar-nav ml-auto">
      <li class="nav-item dropdown">
          <button mat-button [matMenuTriggerFor]="admin">ADMIN</button>
          <mat-menu #admin="matMenu">
            <button mat-menu-item>User Management</button>
          </mat-menu>
      </li>
      <li class="nav-item dropdown">
          <button mat-button [matMenuTriggerFor]="profile">PROFILE</button>
          <mat-menu #profile="matMenu">
            <button mat-menu-item>Change Password</button>
            <button mat-menu-item>Logout</button>
          </mat-menu>
      </li>
    

    Reply

Leave a Comment