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

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

问题描述

这个问题参考了this Github issue,mat-menu 无法使用鼠标悬停切换,我基本上是在尝试用角度材料的菜单替换基于引导程序的水平导航菜单.唯一阻止我复制基于引导程序的菜单是在悬停时打开和关闭 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 关闭,

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 毫秒.

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

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.

推荐答案

第一个挑战是 mat-menu 由于 覆盖层的 z-index...要解决这个问题,您需要在按钮的样式中设置 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;

接下来为两个菜单级别创建 menu 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

下面是如何连接它.

<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天全站免登陆