0
votes

I got menu that is being displayed on mouseenter event of a button. All that in toolbar:

<mat-toolbar color="primary">
  <button mat-button routerLink="/products" [matMenuTriggerFor]="menu1"
      #matMenu1Trigger="matMenuTrigger"
      (mouseenter)="matMenu1Trigger.openMenu()">Menu1
  </button>
  <mat-menu #menu1="matMenu">
    <div (mouseleave)="matMenu1Trigger.closeMenu()">
      <button mat-menu-item>Item 1</button>
    </div>
  </mat-menu>
</mat-toolbar>

The menu is closed when mouseleave event of surrounding span, so far so good. Now I want to close the menu on mouseleave of the triggering button as well by adding

(mouseleave)="matMenu1Trigger.closeMenu()"

When I do this and move mouse over this button, menu starts to flicker, like its being opened/closed every few miliseconds.

Why is that and how to hide the menu when mouse leaves the button?

https://stackblitz.com/edit/angular-kd8bue

Edit: After some googling I found out this behavior is caused by an overlay being displayed when the menu is opened as described here How to open mat-menu on a single click and close other opened menu if there is any?. Not sure if what I would like to achieve is even possible because of the overlay...

2
Do you want to try this with css instead of javascript? - Bagherani
Try to bind menu's opened value to a component typescript variable instead and to change that value from the event.. Like: (HTML) <mat-menu #menu1="matMenu" [opened]="varOpened"> (TYPESCRIPT): var varOpened: boolean = false; (HTML): <div (mouseleave)="closeMenu()"> (TYPESCRIPT):closeMenu(){ this.varOpened=false; }. I don't have tested but I always acted like this way before with Angular Material's stuff so... let me know if it works! - Deadpool
@Deadpool: There is no such property as opened, see material.angular.io/components/menu/api - user1622058

2 Answers

1
votes
    <mat-toolbar color="primary">
        <button   mat-button >Menu0 </button>

        <button mat-button routerLink="/products" [matMenuTriggerFor]="menu1"
        #matMenu1Trigger="matMenuTrigger"
        (mouseenter)="buttonenter()"
        (mouseout)="buttonleave()" style=" z-index: 50000">Menu1
        </button>
        <mat-menu #menu1="matMenu" >
          <div (mouseleave)="matMenu1Trigger.closeMenu()"
          (mouseenter)="menuenter()"
          >
        <button mat-menu-item>Item 1</button>
        <button mat-menu-item>Item 2</button>
        <button mat-menu-item>Item 3</button>
        <button mat-menu-item>Item 4</button>
        <button mat-menu-item>Item 5</button>
        <button mat-menu-item>Item 6</button>        
          </div>
        </mat-menu>

          <button   mat-button >MenuX </button>


          <button mat-button routerLink="/products" [matMenuTriggerFor]="menu2"
        #matMenu1Trigger2="matMenuTrigger"
        (mouseenter)="buttonenter2()"
        (mouseout)="buttonleave2()" style=" z-index: 50000">Menu2
        </button>
        <mat-menu #menu2="matMenu" >
          <div (mouseleave)="matMenu1Trigger2.closeMenu()"
           (mouseenter)="menuenter2()"
          >
        <button mat-menu-item>Item2 1</button>
        <button mat-menu-item>Item2 2</button>
        <button mat-menu-item>Item2 3</button>
        <button mat-menu-item>Item2 4</button>
        <button mat-menu-item>Item2 5</button>
        <button mat-menu-item>Item2 6</button>          

          </div>
        </mat-menu>

          <button   mat-button >Menu3 </button>
          <button   mat-button >Menu4 </button>
          <button   mat-button >Menu5 </button>
          <button   mat-button >Menu6 </button>


      </mat-toolbar>


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

    @Component({
      selector: 'my-app',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.css']
    })
    export class AppComponent {

      name = 'Angular';
      isEnterMatMenuOpen = false;
      @ViewChild('matMenu1Trigger') _matMenu1Trigger
      @ViewChild('menuButton') _menuButton

      isEnterMatMenuOpen2 = false;
      @ViewChild('matMenu1Trigger2') _matMenu1Trigger2
      @ViewChild('menuButton2') _menuButton2

      buttonenter() {
          this._matMenu1Trigger.openMenu();
          this.isEnterMatMenuOpen = false;
      }
      menuenter() {
          this.isEnterMatMenuOpen = true;
      }
      buttonleave() {
        setTimeout(() => {
        if( !this.isEnterMatMenuOpen ) this._matMenu1Trigger.closeMenu();
        }, 50);
      }

      buttonenter2() {
           this._matMenu1Trigger2.openMenu();  
          this.isEnterMatMenuOpen2 = false; 
      }
      menuenter2() {
          this.isEnterMatMenuOpen2 = true;
      }
      buttonleave2() {
        setTimeout(() => {
        if( !this.isEnterMatMenuOpen2 ) this._matMenu1Trigger2.closeMenu();
        }, 50);
      } 

    }
1
votes

Please review this closed issue on github

https://github.com/angular/material2/issues/10378#issuecomment-372626596

When your mouseenter opens the mat-menu... a cdkOverlay is created and focus set to the mat-menu cdkOverlay, this immediately fires your mouseleave event because the focus is now on the overlay and not your button... even though your mouse is still over the button.


Revision

Please review the below stackblitz I did for this SO answer.

How to open and close Angular mat menu on hover

https://stackblitz.com/edit/mat-nested-menu-yclrmd?embed=1&file=app/nested-menu-example.html