0

Objective: To create a custom context menu that can be used across our application. The menu option will have a copy functionality.

My current approach is to create a child component just for the mat-menu but the trigger will come from the parent components that are trying to use this. However I am facing some issue with associating matMenuTriggerFor directive from the trigger element (in my case it is a tag from the parent) with the matMenu directive on the mat-menu (child component). I am getting the following error if I use @ViewChild(MatMenuTrigger) on the child component which I need it for opening the menu. If I change that to @ViewChild(MatMenu) then I am not seeing the issue but the menu does not open.

Error:

Error occurs in the template of component CopyContextMenu.
alkali/apps/mindlab/client/app/tslibs/table_render.ng.html:61:18 - error TS2740: [ngtsc] Type '_MatMenuTriggerBase' is missing the following properties from type 'MatMenuPanel<any>': xPosition, yPosition, overlapTrigger, templateRef, and 3 more.

61                 [matMenuTriggerFor]="copyContextMenu.contextMenuTrigger">
                    ~~~~~~~~~~~~~~~~~

  alkali/apps/mindlab/client/app/tslibs/table_render.ts:15:16
    15   templateUrl: './table_render.ng.html',
                      ~~~~~~~~~~~~~~~~~~~~~~~~

Code Snippet:

Parent component: HTML

<a (contextmenu)="showCopyContextMenu($event, someData)"
    target="_blank">
     someData
</a>

<div [hidden]="!isContextMenuVisible"
        [matMenuTriggerFor]="copyContextMenu.contextMenuTrigger">
        <!-- Error appears above on matMenuTriggerFor directive -->
        <app-copy-context-menu #copyContextMenu
            [contextMenuPosition]="contextMenuPosition"
            [someData]="someData">
        </app-copy-context-menu>
</div>

TS:

@ViewChild(CopyContextMenu, {static: true}) copyContextMenu!: CopyContextMenu;

showCopyContextMenu(event: MouseEvent, someData: string|undefined) {
    event.preventDefault();
    this.contextMenuPosition = {x: event.clientX, y: event.clientY};
    this.isContextMenuVisible = true;
    this.spongeId = someData ?? '';
    this.copyContextMenu.openMenu();

  }

Child Component: HTML

<mat-menu class="copy-context-menu" #contextMenuTrigger="matMenu"
         [style.left]="contextMenuPosition.x"
         [style.top]="contextMenuPosition.y">
  <ng-template matMenuContent>
      <button mat-menu-item (click)="doSomething()">Do Something/button>
  </ng-template>
</mat-menu>

TS:

@Component({
  selector: 'app-copy-context-menu',
  templateUrl: './copy_context_menu.ng.html',
  styleUrls: ['./copy_context_menu.css']
})
export class CopyContextMenu implements OnInit {
  // constructor() {}

  ngOnInit() {}

  @Input() contextMenuPosition = {x: 0, y: 0};
  @Input() someData? = '';
  // If I change this to MatMenu then the error goes away but I can't open the menu either programmatically or automatically menu opens.
  @ViewChild(MatMenuTrigger, {static: true}) contextMenuTrigger!: MatMenuTrigger;


  openMenu() {
     this.contextMenuTrigger.openMenu();
  }

  doSomething() {
    // Implement copy to clipboard method.
    console.log('someData:', this.someData);
    console.log('X, Y', this.contextMenuPosition.x, this.contextMenuPosition.y);
  }
}

1 Answers1

0

You are triggering a MatMenu in the child component from parenting component. Read it as MatMenu

https://stackoverflow.com/a/37476195/15439733

https://stackoverflow.com/a/51132677/15439733

@Component({
  selector: 'app-copy-context-menu',
  templateUrl: './copy_context_menu.ng.html',
  styleUrls: ['./copy_context_menu.css']
})
export class CopyContextMenu implements OnInit {
  // constructor() {}

  ngOnInit() {}

  @Input() contextMenuPosition = {x: 0, y: 0};
  @Input() someData? = '';
  
  //MatMenu
  @ViewChild('matMenu', {read:MatMenu}) contextMenuTrigger: MatMenu;

   openMenu() {
     this.contextMenuTrigger.openMenu();
  }

  doSomething() {
    // Implement copy to clipboard method.
    console.log('someData:', this.someData);
    console.log('X, Y', this.contextMenuPosition.x, this.contextMenuPosition.y);
  }
}
Joosep Parts
  • 5,372
  • 2
  • 8
  • 33
  • If I read it as MatMenu then I won't be able to call openMenu() which I need in my case because it's a contextmenu. I moved the trigger to the child component and it works but would like to understand why I am not able to keep the trigger in parent component and the menu in the child component. – Kishore Khan Jul 22 '23 at 18:08