如何防止 Angular Material 的 mat-menu 菜单关闭?

82

我正在使用 Angular Material 创建一个日期时间选择器控件,并使用以下代码进行操作:

<button mat-button [matMenuTriggerFor]="menu">
    <mat-icon>date_range</mat-icon>
    <span>Date Range</span>
</button>
<mat-menu #menu="matMenu">
    <div fxLayout="row">
        <div fxLayout="column">
            <button (click)="setInterval(15)" mat-menu-item>Last 15 minutes</button>
            <button (click)="setInterval(360)" mat-menu-item>Last 6 hours</button>
            <button (click)="setInterval(1440)" mat-menu-item>Last 24 hours</button>
            <button (click)="setInterval(2880)" mat-menu-item>Last 2 days</button>
            <button (click)="setInterval(10080)" mat-menu-item>Last 7 days</button>
            <button (click)="setInterval(-1)" [matMenuTriggerFor]="dateTimeMenu" mat-menu-item>Custom</button>
        </div>
        <mat-menu class="date-range-menu" #dateTimeMenu="matMenu">
            <div fxLayout="row">
                <div fxLayout="column">
                    <b>From</b>
                    <mat-calendar></mat-calendar>
                </div>
                <div fxLayout="column">
                    <b>To</b>
                    <mat-calendar></mat-calendar>
                </div>
            </div>
        </mat-menu>
    </div>
</mat-menu>

目前,每当我点击一个按钮时,它就会关闭菜单。我知道我们可以在每个mat-menu-item上使用$event.stoppropagation()来防止它关闭。

但是我想知道是否有可能对mat-calendar做同样的操作。

enter image description here

如上图所示,当前选定日期后,它会关闭菜单。是否可能防止这种情况发生?


你好,你成功地制作了日期范围选择器吗?我需要一个,但不想重新发明轮子。 - Parikshit Singh
7个回答

170

您只需将(click)="$event.stopPropagation()"添加到这些日历的父元素中,就像下面这样:

<mat-menu class="date-range-menu" #dateTimeMenu="matMenu">
    <div fxLayout="row">
        <div fxLayout="column" (click)="$event.stopPropagation();">
            <b>From</b>
            <mat-calendar></mat-calendar>
        </div>
        <div fxLayout="column" (click)="$event.stopPropagation();">
            <b>To</b>
            <mat-calendar></mat-calendar>
        </div>
    </div>
</mat-menu>

Stackblitz演示


似乎无法正常工作。当您单击日期时,无法选择它们。 - danbord
1
对我有用。我在mat-menu内添加了一个包装器div,并在其上添加了$event.stoppropagation() - Hamza Iftikhar
它对我在mat-menu中的复选框起作用了,非常感谢你。 - Kedar Km
这种解决方案的另一个问题是,当您在父元素上单击某处时,所有内部元素的 blur 等事件都不会触发。这导致我在单击父元素的其他位置时,mat-autocomplete 没有关闭,因此我必须找到另一种解决方案。 - Raphiki
我刚刚使用了这个解决方案来移除mat-menu。 :P - Vinay Somawat
显示剩余2条评论

18

通过返回到之前的解决方案,并将指令封装在一个方法中,可以避免关闭菜单并继续执行指令。

在 HTML 中:


在 HTML 中:

<mat-menu class="date-range-menu" #dateTimeMenu="matMenu">
    <div fxLayout="row">
        <div fxLayout="column" (click)="doSomething($event);">
            <b>From</b>
            <mat-calendar></mat-calendar>
        </div>
        <div fxLayout="column" (click)="doSomething($event)">
            <b>To</b>
            <mat-calendar></mat-calendar>
        </div>
    </div>
</mat-menu>
IN TS.
doSomething($event:any){
    $event.stopPropagation();
    //Another instructions
}

12

你有很多选择,我邀请您尝试以下选项

    <mat-menu [hasBackdrop]="false">
     <div  (click)="$event.stopPropagation()" (keydown)="$event.stopPropagation()">
     ...
     </div>
    </mat-menu>

如果您想防止在单击框外的任何位置时关闭mat-menu,则使用[hasBackdrop]="false",否则删除它。


6
如果您希望停止在单击mat-menu-content时关闭mat-menu,我可以提供一些技巧。可以在锚标记上添加$event.stopPropogation()而不是在mat-menu上添加,这样即使在表单的任何位置单击,菜单对话框也不会关闭。
Example:- 
    <mat-menu #nameAndDescriptioContextMenu="matMenu" [hasBackdrop]="false">
         <a (click)="$event.stopPropagation();$event.preventDefault();">
               <div>
                 Form Group Form
               </div> 
         </a> 
    </mat-menu>

干得好,兄弟。做得不错! - Shakir Ahmed

2
很遗憾,以上答案都对我没有用。在需要菜单面板比内容宽得多的情况下,您无法放置"$event.stopPropagation();" ,因此如果单击mat-menu-panel,它将关闭。 幸运的是,仍然有一种方法可以避免这种情况,即通过“覆盖”MatMenu的click事件。 以下是一个stackblitz示例,感谢我的同事:https://stackblitz.com/edit/mat-menu-disable-close

ngAfterViewInit() {
    // Inject our custom logic of menu close
    (this.searchMenu as any).closed = this.searchMenu.close = this.configureMenuClose(this.searchMenu.close);
  }

private configureMenuClose(old: MatMenu['close']): MatMenu['close'] {
    const upd = new EventEmitter();
    feed(upd.pipe(
      filter(event => {
        if (event === 'click') {
          // Ignore clicks inside the menu 
          return false;
        }
        return true;
      }),
    ), old);
    return upd;
  }
}
function feed<T>(from: Observable<T>, to: Subject<T>): Subscription {
  return from.subscribe(
    data => to.next(data),
    err => to.error(err),
    () => to.complete(),
  );
}

这样,它只会在你点击外部(这很容易移除)或者使用触发器时关闭。 这就是我在项目中想要的行为,希望对某些人有用。

2
尝试这种方式,将所有的 mat 菜单项包裹在一个 div 中,并停止传播任何点击事件。
<mat-menu #menu="matMenu" [hasBackdrop]="false">
      <div (click)="$event.stopPropagation()" (keydown)="$event.stopPropagation()">
        <button mat-menu-item>
          <mat-icon>dialpad</mat-icon>
          <span>Origin</span>
        </button>
        <button mat-menu-item disabled>
          <mat-icon>voicemail</mat-icon>
          <span>Check voice mail</span>
        </button>
        <button mat-menu-item>
          <mat-icon>notifications_off</mat-icon>
          <span>Disable alerts</span>
        </button>
      </div>
    </mat-menu>

1
你可以直接在组件中使用此指令。
在HTML中。
<mat-menu class="date-range-menu" #dateTimeMenu="matMenu">
    <div fxLayout="row">
        <div fxLayout="column" mat-filter-item>
            <b>From</b>
            <mat-calendar></mat-calendar>
        </div>
        <div fxLayout="column" mat-filter-item >
            <b>To</b>
            <mat-calendar></mat-calendar>
        </div>
    </div>
</mat-menu>

请将其保存为filter.directive.ts文件 import { Directive, HostListener, HostBinding } from "@angular/core";

@Directive({
  selector: "[mat-filter-item]"
})
export class FilterItemDirective {
  @HostListener("click", ["$event"])
  onClick(e: MouseEvent) {
    e.stopPropagation();
    e.preventDefault();

    return false;
  }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接