6

I'm using MatMenu as a popup to tour around new users on my web app so I don't want to dismiss it whenever the user clicks outside of it.

I've already used used $event.stopPropagation() to stop it from closing when I click a button inside it. Now I want to know how to keep it open even if you click outside of it.

FledglingDeveloper
  • 381
  • 4
  • 6
  • 12
  • Here is reference same question asked. https://stackoverflow.com/questions/46967970/how-to-prevent-angular-material-mat-menu-from-closing – Rahul Gupta Oct 08 '19 at 17:17
  • Thank you but I've already seen that. The provided solution there was for stopping mat-menu from closing when clicking INSIDE. My question is how to stop mat-menu from closing when you click OUTSIDE of it (menu). – FledglingDeveloper Oct 08 '19 at 17:22

4 Answers4

7

i think [hasBackdrop]="false" and [hasBackdrop]="true" can handle that

Ajay Gangarde
  • 311
  • 4
  • 2
  • 3
    this should be the real answer, but just to add one extra note you need to set the [hasBackdrop]="false", in order to prevent the menu from closing when click outside of it. – Nabz Sep 03 '20 at 02:20
7

You can wrap around the mat-menu-items if you are trying to stop propagating the whole menu.

<mat-menu [hasBackdrop]="false">
     <div  (click)="$event.stopPropagation()" (keydown)="$event.stopPropagation()">
       <your-app></your-app>
     </div>
</mat-menu>
John Kuriakose
  • 4,065
  • 2
  • 13
  • 20
5

The click is handled by the overlay backdrop. You can apply/remove classes to the backdrop dynamically based on your menu open and close, and defeat the backdrop click using CSS with pointer-events.

For example:

HTML

<button mat-button [matMenuTriggerFor]="menu" (menuOpened)="preventCloseOnClickOut()" (menuClosed)="allowCloseOnClickOut()">Menu</button>

TS

export class MenuOverviewExample {
  constructor(private overlayContainer: OverlayContainer) {}

  preventCloseOnClickOut() {
    this.overlayContainer.getContainerElement().classList.add('disable-backdrop-click');
  }

  allowCloseOnClickOut() {
    this.overlayContainer.getContainerElement().classList.remove('disable-backdrop-click');
  }
}

Global CSS

.disable-backdrop-click .cdk-overlay-backdrop.cdk-overlay-transparent-backdrop.cdk-overlay-backdrop-showing {
  pointer-events: none;
}
Rahul Gupta
  • 991
  • 4
  • 12
G. Tranter
  • 16,766
  • 1
  • 48
  • 68
  • 2
    Thank you this worked for me. I'd just like to add that you need to import { OverlayContainer } from "@angular/cdk/overlay"; first in your TS file. – FledglingDeveloper Oct 09 '19 at 03:12
0

I had the same problem, I had need a validation and as result the outside click (on backdrop) shouldn't effect. Remove the backdrop events with pointer-events allows interactions in all behind element, my solution was create a clone of backdrop and remove the listeners of this clone.

    avoidClickOutside() {
      const overlayers = this.overlay
      .getContainerElement()
      .querySelectorAll('.controlled');

      if (overlayers && this.isAvoidClickOutside) {
        overlayers.forEach((element) => {
          const clone = element.cloneNode(true);
          (clone as Element).classList.add('clone');
          clone.addEventListener('click', (event: Event) => {
            if (confirm('Not allowed... Do you want remove this behaviour?!')) {
              this.removeCloneBackdrop();
            }
          });
          element.parentNode.insertBefore(clone, element.nextSibling);
        });
      }
   }

isAvoidClickOutside is my control variable to either avoid or not.

Available: https://stackblitz.com/edit/angular-yjvkft