如何在悬停时打开和关闭Angular Mat菜单 [英] How to open and close Angular mat menu on hover

查看:122
本文介绍了如何在悬停时打开和关闭Angular Mat菜单的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

此问题是有关 Github问题的参考,其中mat-menu可以不能使用鼠标悬停来切换,我基本上是在尝试将基于bootstrap的水平导航菜单替换为有角材质的菜单.使我无法复制基于引导程序的菜单的唯一方法是在悬停时打开和关闭mat-menu. 如上述Github问题所述,有一些解决方法可以实现我想要的功能,例如使用 mouseEnter

This question is in reference to this Github issue, with mat-menu which can't be toggled using mouse hover, I am basically trying to replace a bootstrap based horizontal navigation menu with angular material's menu. The only thing keeping me from replicating bootstrap based menu is opening and closing mat-menu on hover. As mentioned in the above Github issue there are some workarounds to achieve what I want, like using, mouseEnter

(mouseenter)="menuTrigger.openMenu()"

或在Mat-menu菜单中添加 span 以便绑定mat-menu close,

or adding a span inside Mat-menu in order to bind mat-menu close,

<mat-menu #menu="matMenu" overlapTrigger="false">
  <span (mouseleave)="menuTrigger.closeMenu()">
    <button mat-menu-item>Item 1</button>
    <button mat-menu-item>Item 2</button>
  </span>
</mat-menu>

但是,所有解决方案似乎都无法解决所有小问题,

but none of the solutions seems to cover every little scenario,

例如

如上述Github问题中所述,第一个SO解决方案中存在以下问题.

As mentioned in the above Github issue, there are following issues in the first SO solution.

  • 将鼠标光标悬停在按钮上,将弹出菜单.但是,如果您单击该按钮,它将隐藏并显示菜单.恕我直言 这是一个错误.
  • 要隐藏菜单,用户需要在菜单外部单击.理想情况下,如果鼠标光标位于菜单外,菜单将变为隐藏
    该区域的位置(包括按钮,菜单和子菜单)
    超过400毫秒.
  • Hover the mouse cursor on the button and the menu will pop up. But if you click on the button, it will hide and show the menu. IMHO it's a bug.
  • To hide the menu, the user needs to click outside of the menu. Ideally, the menu would become hidden if the mouse cursor is outside
    of the area (which includes the button, the menu, and sub-menus)
    longer than 400ms.

在跨度解决方案中,该解决方案试图解决上述问题之一,但无法正常工作,例如

And in the span solution which tries to solve one of the above issues, but doesn't work properly, e.g.

将鼠标悬停在MatMenuTrigger上确实会按预期打开mat-menu,但是如果用户在不输入mat-menu的情况下将鼠标移开,则它不会自动关闭,这是错误的.

hovering over MatMenuTrigger does open the mat-menu as expected but if a user moves the mouse away without entering mat-menu, then it doesn't close automatically which is wrong.

也移至第二级子菜单之一也会关闭我不想要的第一级菜单,

Also moving to one of the levels two sub-menu also closes the level one menu which is not what I want,

P.S将鼠标从一个打开的菜单移动到下一个同级菜单不会打开下一个菜单.我猜这可能很难实现,如在此处所述,但是我认为其中一些可能可以实现,对吗?

P.S moving mouse from one opened menu to the next sibling one doesn't open the next one. I guess this might be difficult to achieve as mentioned here, But I think some of these might be achievable right?

这是一个基本的 stackBlitz ,它重现了我的经验,对您的帮助表示感谢

Here is a basic stackBlitz which reproduces what I am experiencing, any help is appreciated.

推荐答案

第一个挑战是,由于覆盖的z-index生成CDK覆盖时,mat-menu会从按钮上窃取焦点.解决此问题,您需要为按钮设置样式的z-index ...

The first challenge is that mat-menu steals the focus from the button when the CDK overlay is generated due to the z-index of the overlay... to solve this you need to set the z-index in a style for the button...

  • 当您将(mouseleave)添加到按钮时,这将停止递归循环. style="z-index:1050"
  • This will stop the recursive loop when you add a (mouseleave) to the button. style="z-index:1050"

接下来,您需要跟踪levelonelevelTwo菜单的所有进入和离开事件的状态,并将该状态存储在两个组件变量中.

Next you need to track the state of all enter and leave events for the levelone and levelTwo menu's and store that state in two component variables.

enteredButton = false;
isMatMenuOpen = false;
isMatMenu2Open = false;

接下来为两个菜单级别创建菜单enter和menuLeave方法.注意menuLeave(trigger)检查是否访问了level2,如果为true,则不执行任何操作.

Next create menu enter and menuLeave methods for both menu levels.. notice menuLeave(trigger) checks if level2 is accessed and does nothing if true.

请注意: menu2Leave()具有允许导航回到第一级的逻辑,但是如果退出另一侧则将其关闭...也可以在离开水平时取消按钮的焦点.

Please Note: menu2Leave() has logic to allow navigation back to level one but close both if exit the other side... also removing button focus upon leave of levels.

menuenter() {
    this.isMatMenuOpen = true;
    if (this.isMatMenu2Open) {
      this.isMatMenu2Open = false;
    }
  }

  menuLeave(trigger, button) {
    setTimeout(() => {
      if (!this.isMatMenu2Open && !this.enteredButton) {
        this.isMatMenuOpen = false;
        trigger.closeMenu();
        this.ren.removeClass(button['_elementRef'].nativeElement, 'cdk-focused');
        this.ren.removeClass(button['_elementRef'].nativeElement, 'cdk-program-focused');
      } else {
        this.isMatMenuOpen = false;
      }
    }, 80)
  }

  menu2enter() {
    this.isMatMenu2Open = true;
  }

  menu2Leave(trigger1, trigger2, button) {
    setTimeout(() => {
      if (this.isMatMenu2Open) {
        trigger1.closeMenu();
        this.isMatMenuOpen = false;
        this.isMatMenu2Open = false;
        this.enteredButton = false;
        this.ren.removeClass(button['_elementRef'].nativeElement, 'cdk-focused');
        this.ren.removeClass(button['_elementRef'].nativeElement, 'cdk-program-focused');
      } else {
        this.isMatMenu2Open = false;
        trigger2.closeMenu();
      }
    }, 100)
  }

  buttonEnter(trigger) {
    setTimeout(() => {
      if(this.prevButtonTrigger && this.prevButtonTrigger != trigger){
        this.prevButtonTrigger.closeMenu();
        this.prevButtonTrigger = trigger;
        trigger.openMenu();
      }
      else if (!this.isMatMenuOpen) {
        this.enteredButton = true;
        this.prevButtonTrigger = trigger
        trigger.openMenu()
      }
      else {
        this.enteredButton = true;
        this.prevButtonTrigger = trigger
      }
    })
  }

  buttonLeave(trigger, button) {
    setTimeout(() => {
      if (this.enteredButton && !this.isMatMenuOpen) {
        trigger.closeMenu();
        this.ren.removeClass(button['_elementRef'].nativeElement, 'cdk-focused');
        this.ren.removeClass(button['_elementRef'].nativeElement, 'cdk-program-focused');
      } if (!this.isMatMenuOpen) {
        trigger.closeMenu();
        this.ren.removeClass(button['_elementRef'].nativeElement, 'cdk-focused');
        this.ren.removeClass(button['_elementRef'].nativeElement, 'cdk-program-focused');
      } else {
        this.enteredButton = false;
      }
    }, 100)
  }

HTML

下面是如何将它们全部连接起来的方法.

below is how to wire it all up.

<ng-container *ngFor="let menuItem of modulesList">

    <ng-container *ngIf="!menuItem.children">
        <a class="nav-link">
            <span class="icon fa" [ngClass]="menuItem.icon"></span>
      <span class="text-holder">{{menuItem.label}}</span>
    </a>
  </ng-container>
  <ng-container *ngIf="menuItem.children.length > 0">
    <button #button mat-button [matMenuTriggerFor]="levelOne" #levelOneTrigger="matMenuTrigger" (mouseenter)="levelOneTrigger.openMenu()" (mouseleave)="buttonLeave(levelOneTrigger, button)" style="z-index:1050">
      <span class="icon fa" [ngClass]="menuItem.icon"></span>
      <span>{{menuItem.label}}
        <i class="fa fa-chevron-down"></i>
      </span>
    </button>

    <mat-menu #levelOne="matMenu" direction="down" yPosition="below">
      <span (mouseenter)="menuenter()" (mouseleave)="menuLeave(levelOneTrigger, button)">
      <ng-container *ngFor="let childL1 of menuItem.children">
        <li class="p-0" *ngIf="!childL1.children" mat-menu-item>
          <a class="nav-link">{{childL1.label}}
            <i *ngIf="childL1.icon" [ngClass]="childL1.icon"></i>
          </a>
        </li>
        <ng-container *ngIf="childL1.children && childL1.children.length > 0">
          <li mat-menu-item #levelTwoTrigger="matMenuTrigger" [matMenuTriggerFor]="levelTwo">
            <span class="icon fa" [ngClass]="childL1.icon"></span>
            <span>{{childL1.label}}</span>
          </li>

          <mat-menu #levelTwo="matMenu">
            <span (mouseenter)="menu2enter()" (mouseleave)="menu2Leave(levelOneTrigger,levelTwoTrigger, button)">
            <ng-container *ngFor="let childL2 of childL1.children">
              <li class="p-0" mat-menu-item>
                <a class="nav-link">{{childL2.label}}
                  <i *ngIf="childL2.icon" [ngClass]="childL2.icon"></i>
                </a>
              </li>
            </ng-container>
            </span>
          </mat-menu>
        </ng-container>
      </ng-container>
      </span>
    </mat-menu>
  </ng-container>

</ng-container>

Stackblitz

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

这篇关于如何在悬停时打开和关闭Angular Mat菜单的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆