10

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:


Artanis Zeratul
  • 963
  • 2
  • 14
  • 40

6 Answers6

22

The correct solution for this problem:

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

//And call:

me.trigger.toArray()[indexOfMenu].openMenu();
brooksrelyt
  • 3,925
  • 5
  • 31
  • 54
László Leber
  • 281
  • 2
  • 5
  • 1
    This should be marked as the correct answer. Thank you! – Naser AlOqab Sep 12 '19 at 05:38
  • 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? – Ido Mar 04 '20 at 09:32
  • 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. – Michael Barrett Jun 30 '20 at 15:31
  • The query list order is not guaranteed if elements are added later/dynamically. If that is your case, check @andrea-r answer below. – kontiki Nov 29 '21 at 09:25
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>
nontechguy
  • 751
  • 5
  • 21
  • @ArtanisZeratul if it helped to fix your issue please do we a favour and click the arrow to mark as useful, thanks. – nontechguy Jul 18 '18 at 12:32
  • What if I want to use 2 matmenu in single component? – Maulik Nov 20 '18 at 11:09
  • @Maulik do you mean a nested matmenu - https://material.angular.io/components/menu/overview – nontechguy Nov 29 '18 at 06:24
  • 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. – Maulik Nov 30 '18 at 10:22
  • @Maulik 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. – nontechguy Nov 30 '18 at 10:43
  • 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? – Maulik Nov 30 '18 at 11:49
  • It will be helpfull if you gives your code so that I can observed it how you achieved. – Maulik Nov 30 '18 at 11:51
  • @Maulik I have the same issue. The answer by László Leber worked perfectly. Just make sure you import ViewChildren and QueryList from @angular/core. – Naser AlOqab Sep 12 '19 at 05:40
4

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

Andrea R.
  • 141
  • 2
  • 6
  • 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. – kontiki Nov 29 '21 at 09:15
  • This is the only valid response! it should be upvoted!! – Andrea Nicola Feb 18 '22 at 11:35
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.

SAURABH SINGH
  • 121
  • 1
  • 1
  • 6
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.

Quentin
  • 1,865
  • 2
  • 24
  • 40
nontechguy
  • 751
  • 5
  • 21
  • 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. – Maulik Nov 30 '18 at 12:11
  • @non_tech_guy, heaps of thanks. I will try to look at your answer later and give you feedback. cheers! – Artanis Zeratul Dec 02 '18 at 05:38
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>