Angular Material: exclude elements from context menu

122 views Asked by At

I'm using the Angular Material CDK to implement a context menu in my application, as documented here:

https://v14.material.angular.io/cdk/menu/overview#context-menus

The menu works as described, however I have an issue. I am in a situation where I need to exclude some html elements from triggering the context menu. For example, let's see a basic use case:

<form [cdkContextMenuTriggerFor]="context_menu">
  <div>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo</div>

  <input type="text"> <!-- how to exclude this input from triggering the context menu? -->

  <div>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo</div>
</form>

<ng-template #context_menu>
  <div class="example-menu" cdkMenu>
      <button class="example-menu-item" cdkMenuItem>Cut</button>
      <button class="example-menu-item" cdkMenuItem>Copy</button>
      <button class="example-menu-item" cdkMenuItem>Link</button>
  </div>
</ng-template>

Here, I would like to have my custom context menu work on the entire form but NOT on the input. If the user right-clicks inside the input, I would like to show the classic browser context menu instead of my custom one.

Can this be done somehow? Maybe with a special directive on the element that I want to exclude or something like that? And if not, can someone recommend an Angular library that allows this customization?

2

There are 2 answers

0
Chellappan வ On BEST ANSWER

Add (contextmenu)="$event.stopPropagation()" event on element you want to exclude from cdk context menu click

<form [cdkContextMenuTriggerFor]="outer">
  <div>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo</div>

  <input type="text" (contextmenu)="$event.stopPropagation()"> 

  <div>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo</div>
</form>

Example

0
Eliseo On

I love Chellappan solution, but I can not resist to answer another approach:

You can subscribe to document.mousedown and enable/disable the cdkContextMenuTriggerFor. For this we need get two ViewChild

  @ViewChild(CdkContextMenuTrigger) menutrigger: CdkContextMenuTrigger;
  @ViewChild(CdkContextMenuTrigger, { read: ElementRef }) menuDiv: ElementRef;

Then, in ngOnInit, you can use

  ngOnInit() {
    fromEvent(document, 'mousedown')
      .pipe(
        filter((event: any) =>
          this.menuDiv.nativeElement.contains(event.target)
        )
      )
      .subscribe((event: any) => {
        if (this.menutrigger)
        {
          //I check if has a class, but we can check the tagName
          //or another condition
          //e.g. 
          //    this.menutrigger.disabled =
          //       event.target.tagName=='INPUT'

          this.menutrigger.disabled =
            event.target.classList.contains('no-context-menu');
        }
      });
  }

The only is add a class "no-context-menu" to your input or div

<input class="no-context-menu">

stackblitz