How to prevent angular material mat-menu from closing?

JavascriptAngularAngular Material2

Javascript Problem Overview


I'm creating a date time picker control in the angular material and having the below code to do that

<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>

Currently when ever I click a button it is closing the menu. I know we can do $event.stoppropagation() on each mat-menu-item to prevent it from closing.

But I want to know is it possible to do that for mat-calendar

enter image description here

As you can see in the above image currently when i select a date it is closing the menu. Is it possible to prevent that?

Javascript Solutions


Solution 1 - Javascript

You just add (click) = "$event.stopPropagation()" to the parent element of these calendars. Like below,

<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 demo.

Solution 2 - Javascript

By giving a return to the previous solution, encapsulating the instruction in a method allows not to close the menu and continue executing instructions

IN 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
}

Solution 3 - Javascript

if You want to stop closing mat-menu even on clicking on mat-menu-content i did hack as added $event.stopPropogation() on a anchor tag instead of mat-menu. so Menu dailog will not close even if clicked anywhere on the form.

Example:- 
    <mat-menu #nameAndDescriptioContextMenu="matMenu" [hasBackdrop]="false">
         <a (click)="$event.stopPropagation();$event.preventDefault();">
               <div>
                 Form Group Form
               </div> 
         </a> 
    </mat-menu>

Solution 4 - Javascript

You have many options, I invite you try the following

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

the [hasBackdrop]="false" if you want to prevent closing mat-menu when clicking anywhere outside the box, otherwise remove it

Solution 5 - Javascript

Unfortunately, none of the above answers worked for me. In cases when you need menu panel to be much wider than the content, there is no place you can put "$event.stopPropagation();" on, so if you click on mat-menu-panel it will close. Luckily, there is still a way to avoid this, by 'overriding' click event of MatMenu. Here is stackblitz example, thanks to my colleague: 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(),
  );
}

This way, it will close only if you click outside (that's an easy to remove) and if you use trigger. That is the behavior I wanted in my project and I hope it will be useful for someone.

Solution 6 - Javascript

you can use this directive directly in your component.

in 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>

save it as 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;
  }
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionUmamaheswaranView Question on Stackoverflow
Solution 1 - JavascriptAnikView Answer on Stackoverflow
Solution 2 - JavascriptJLazar0View Answer on Stackoverflow
Solution 3 - JavascriptAjay GangardeView Answer on Stackoverflow
Solution 4 - JavascriptSmaillnsView Answer on Stackoverflow
Solution 5 - JavascriptAmarulaView Answer on Stackoverflow
Solution 6 - Javascriptgaurav sharmaView Answer on Stackoverflow