使用Mat-menu作为上下文菜单:如何关闭打开的Mat-menu,然后在其他位置打开它

时间:2019-10-11 16:02:31

标签: angular angular-material contextmenu

角度: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()

    }
}

5 个答案:

答案 0 :(得分:1)

我的解决方案

  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

答案 1 :(得分:0)

只需添加hasBackdrop = false即可。 发生的情况是,当我右键单击菜单后打开菜单时,将出现一个不可见的背景覆盖,覆盖整个页面。难怪无论我如何再次尝试右键单击,contextmenu事件都不会触发,因此不会调用onTriggerContextMenu()。通过将hasBackgrop设置为false,该覆盖图将不会显示。

答案 2 :(得分:0)

也许您已经解决了,但是对于其他人,这是我解决此问题的方法: 环绕div中的菜单项,当鼠标离开菜单(该div)时,我将其关闭,因此您可以立即再次右键单击另一个位置以再次显示上下文菜单...。

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

答案 3 :(得分:0)

  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();
    }

答案 4 :(得分:0)

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

// 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";