【问题标题】:Using mat-menu as context menu: How to close opened mat-menu, and open it in another location使用 mat-menu 作为上下文菜单:如何关闭打开的 mat-menu,并在另一个位置打开它
【发布时间】:2020-02-09 04:41:51
【问题描述】:

角度:v8.2.8

我设法使用 mat-menu(角度材料)来模拟上下文菜单。在我的页面上右键单击,菜单出现在我的鼠标位置。

我想要实现的目标:当我将鼠标移动到页面上的另一个位置时,我再次右键单击。我希望原始位置的菜单关闭并在新位置重新打开。

但实际上:当我右键单击时,没有任何反应,并且原始位置的菜单保持打开状态。

其实我要在原来的位置左键关闭打开的菜单,然后在新的鼠标位置右击打开菜单。

问题:知道如何实现我想要的吗?

@Component({
  selector: 'fl-home',
  template:`
 <mat-menu #contextmenu>
      <div >
           <button mat-menu-item>Clear all Paths</button>
       </div>
  </mat-menu>
  <div [matMenuTriggerFor]="contextmenu" [style.position]="'absolute'" [style.left.px]="menuX" [style.top.px]="menuY" ></div>
  
  <div class="container" (contextmenu)="onTriggerContextMenu($event);"> ....</div>
`})
export class HomeComponent  {

    menuX:number=0
    menuY:number=0

    @ViewChild(MatMenuTrigger,{static:false}) menu: MatMenuTrigger; 
    
   
    onTriggerContextMenu(event){
        
      event.preventDefault();
      this.menuX = event.x - 10;
      this.menuY = event.y - 10;
      this.menu.closeMenu() // close existing menu first.
      this.menu.openMenu()

    }
}

【问题讨论】:

    标签: angular angular-material contextmenu


    【解决方案1】:

    只需添加hasBackdrop=false,如&lt;mat-menu ... [hasBackdrop]="false"&gt;。 发生的情况是,当我右键单击后打开菜单时,会出现一个覆盖整个页面的不可见背景覆盖。无论我尝试再次右键单击什么,上下文菜单事件都不会触发,因此不会调用 onTriggerContextMenu()。通过将hasBackgrop 设置为false,将不会出现此叠加层。

    【讨论】:

    • 当在菜单上将该属性设置为 false 时,无论我点击哪里,菜单都不会关闭。这行得通吗?
    • 这不是一个好的解决方案。菜单必须关闭。
    【解决方案2】:

    我的解决方案

      ngOnInit(): void {
        this.windowClickSubscription = fromEvent(window, 'click').subscribe((_) => {
          if (this.contextMenu.menuOpen) {
            this.contextMenu.closeMenu();
          }
        });
      }
      ngOnDestroy(): void {
        this.windowClickSubscription && this.windowClickSubscription.unsubscribe();
        this.menuSubscription && this.menuSubscription.unsubscribe();
      }
      onContextMenu(event: MouseEvent){
        event.preventDefault();
        this.menuSubscription && this.menuSubscription.unsubscribe();
        this.menuSubscription = of(1)
          .pipe(
            tap(() => {
              if (this.contextMenu.menuOpen) {
                this.contextMenu.closeMenu();
              }
              this.contextMenuPosition.x = event.clientX;
              this.contextMenuPosition.y = event.clientY;
            }),
            // delay(this.contextMenu.menuOpen ? 200 : 0),
            delayWhen((_) => (this.contextMenu.menuOpen ? interval(200) : of(undefined))),
            tap(async() => {
              this.contextMenu.openMenu();
              let backdrop: HTMLElement = null;
              do {
                await this.delay(100);
                backdrop = document.querySelector(
                  'div.cdk-overlay-backdrop.cdk-overlay-transparent-backdrop.cdk-overlay-backdrop-showing'
                ) as HTMLElement;
              } while (backdrop === null);
              backdrop.style.pointerEvents = 'none';
            })
          )
          .subscribe();
      }
      delay(delayInms: number) {
        return new Promise((resolve) => {
          setTimeout(() => {
            resolve(2);
          }, delayInms);
        });
      }
    

    https://stackblitz.com/edit/angular-ivy-udzyez

    【讨论】:

    • 我看到了一个有问题的动画,这是预期的行为吗?
    • 这个只出现在stackblitz中,实际环境中不会出现。你可以试试
    • @Jay 我的应用程序也发生了故障行为。
    【解决方案3】:

    也许你解决了它,但对于其他人来说,这是我解决这个问题的方法: 围绕 div 中的菜单项,当鼠标离开菜单(那个 div)时我正在关闭它,所以你可以立即在另一个地方再次右键单击以再次显示上下文菜单....

    <div (mouseleave)="mouseLeaveMenu()">
      ...menu items...
    </div>
    ...
    mouseLeaveMenu() {
      if (this.contextMenu.menuOpened) this.contextMenu.closeMenu();
    }
    

    【讨论】:

    • 感谢您的回复。但是你的方法并没有达到我的目的,就是当鼠标离开原来的菜单时,我可能还想让它继续打开,以防我改变主意,想继续使用原来的菜单。
    【解决方案4】:
    1. 将属性[hasBackdrop]="false"添加到mat-menu
    2. 更新onTriggerContextMenu()函数如下所示
    3. 最后;要关闭菜单,请在您拥有(contextmenu)="..." 触发器的相同元素上正常单击,同时添加(click)="themenu.closeMenu();"
        onTriggerContextMenu(event: MouseEvent) {
            this.themenu.closeMenu();
            event.preventDefault();
            this.menuX = event.clientX;
            this.menuY = event.clientY;
            this.themenu.menu.focusFirstItem('mouse');
            this.themenu.openMenu();
        }
    

    【讨论】:

      【解决方案5】:

      如果您不想像大多数其他答案一样首先关闭菜单,您可以手动设置菜单的位置:

      // Assume you have the position in variables x and y
      // Additionally you need to have the MatMenuTrigger (in this example in variable matMenuTrigger)
      
      // this.cd.markForCheck() is important here if you just opened the menu with matMenuTrigger.openMenu()
      
      const panelID = matMenuTrigger.menu.panelId;
      const panelElement: HTMLElement = document.getElementById(panelID);
      
      panelElement.style.position = "absolute";
      panelElement.style.left = x + "px";
      panelElement.style.top = y + "px";
      

      【讨论】:

        猜你喜欢
        • 2018-07-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-01-09
        • 1970-01-01
        • 1970-01-01
        • 2019-05-26
        • 2018-12-24
        相关资源
        最近更新 更多