Angular 5 Material Multiple mat-menu
Asked Answered
C

6

11

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:


Contraposition answered 12/7, 2018 at 23:55 Comment(0)
M
8

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>
Mesopause answered 15/7, 2018 at 12:11 Comment(8)
@ArtanisZeratul if it helped to fix your issue please do we a favour and click the arrow to mark as useful, thanks.Mesopause
What if I want to use 2 matmenu in single component?Carisa
@Carisa do you mean a nested matmenu - material.angular.io/components/menu/overviewMesopause
No. I want to apply two context menus in single component. But it's only triggered first one not second one. The scenario is : on right click of table row, I want to open a matmenu and I have another table on which right click I want to open second matmenu. But when I right click on either one of them, it only triggered first matMenu.Carisa
@Carisa then my original solution should work. Create two separate components. I have two matmenu's in my top toolbar and each one is a separate component that include the matmenu and the button.Mesopause
But bro I have around 10 menus inside my application. So, should I create 10 different components for it? How to pass data from one to another its also a requirement to pass data on click of that each menu items?Carisa
It will be helpfull if you gives your code so that I can observed it how you achieved.Carisa
@Carisa I have the same issue. The answer by László Leber worked perfectly. Just make sure you import ViewChildren and QueryList from @angular/core.Intrusion
S
22

The correct solution for this problem:

@ViewChildren(MatMenuTrigger) trigger: QueryList<MatMenuTrigger>;

//And call:

me.trigger.toArray()[indexOfMenu].openMenu();
Sharpedged answered 5/2, 2019 at 19:13 Comment(4)
This should be marked as the correct answer. Thank you!Intrusion
This is what im looking for. but it seems to not work. the trigger is of type MatMenuTrigger and not querylist of that type. Do you know why?Glorianna
This worked perfectly! Since the triggers are generated by an *ngFor statement along with the menu target, I supplied the index to the menu function as mentioned in the answer, allowing it to select the appropriate trigger from the array.Hamer
The query list order is not guaranteed if elements are added later/dynamically. If that is your case, check @andrea-r answer below.Renown
M
8

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>
Mesopause answered 15/7, 2018 at 12:11 Comment(8)
@ArtanisZeratul if it helped to fix your issue please do we a favour and click the arrow to mark as useful, thanks.Mesopause
What if I want to use 2 matmenu in single component?Carisa
@Carisa do you mean a nested matmenu - material.angular.io/components/menu/overviewMesopause
No. I want to apply two context menus in single component. But it's only triggered first one not second one. The scenario is : on right click of table row, I want to open a matmenu and I have another table on which right click I want to open second matmenu. But when I right click on either one of them, it only triggered first matMenu.Carisa
@Carisa then my original solution should work. Create two separate components. I have two matmenu's in my top toolbar and each one is a separate component that include the matmenu and the button.Mesopause
But bro I have around 10 menus inside my application. So, should I create 10 different components for it? How to pass data from one to another its also a requirement to pass data on click of that each menu items?Carisa
It will be helpfull if you gives your code so that I can observed it how you achieved.Carisa
@Carisa I have the same issue. The answer by László Leber worked perfectly. Just make sure you import ViewChildren and QueryList from @angular/core.Intrusion
R
7

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

Remaremain answered 13/1, 2021 at 14:23 Comment(2)
I'm glad I read to the end! This answer is exactly right! Thanks! Some of the other answers trust the QueryList order, but it cannot be trusted when elements are added later.Renown
This is the only valid response! it should be upvoted!!Alehouse
B
3

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

The trick to do is to create a separate 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.

Brotherhood answered 16/2, 2021 at 8:54 Comment(0)
M
2

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.

Mesopause answered 30/11, 2018 at 11:5 Comment(2)
ok it seems for one matMenu you only showed details on it and on another menu you only have to navigate "Update profile" and "Account Settings". By the way thanks for code. Will see whether I can achieved that on single component or not.Carisa
@non_tech_guy, heaps of thanks. I will try to look at your answer later and give you feedback. cheers!Contraposition
M
1
<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>

Malita answered 8/3, 2019 at 15:6 Comment(1)
For completeness it is good to explain why and how this solves the problemDevastating

© 2022 - 2024 — McMap. All rights reserved.