2
votes

I would like to position a mat menu popup relative to a parent element.

Angular's documentation says the following about mat menu position:

By default, the menu will display below (y-axis), after (x-axis), without overlapping its trigger

This is very nice but it's relative to the trigger (matMenuTriggerFor).

I want the menu to open relative to the parent element. Click on button inside div, mat men displays without overlapping it's relative element.

    <gridster-item [item]="w" [class.editing]="w.editing">
      <div class="primary-header widget-settings-bar" color="primary" *ngIf="showSettingsBar == true">
        <div class="widget-settings-button contrast-color">
          <mat-icon class="icon" aria-hidden="false" aria-label="Delete" [matMenuTriggerFor]="appMenu">settings
          </mat-icon>
        </div>
        <div class="widget-settings-button contrast-color">
          <mat-icon class="icon" aria-hidden="false" aria-label="Duplicate" (click)="duplicateDashboardWidget(w)">
            control_point_duplicate</mat-icon>
        </div>
        <div class="widget-settings-button contrast-color">
          <mat-icon class="icon" aria-hidden="false" aria-label="Delete" (click)="deleteDashboardWidget(w)">delete
          </mat-icon>
        </div>
      </div>
      <div class="grid-widget mat-card2">
        <ndc-dynamic [ndcDynamicComponent]=components[w.component] [ndcDynamicInputs]="w"></ndc-dynamic>
      </div>
    </gridster-item>

Here is the widget component I'm talking about. Unopened Component (hovering)

Here is what happens when you click the matMenuTriggerFor that triggers menu. notice it's overlapping the parent div. Triggered from button

This is what I want. The menu pops up relative to the parent element. Notice it's not overlapping the div because I put the matMenuTriggerFor on the gridster-item parent.

Triggered from div

Problem is I can't figure out how to make this menu open without matMenuTriggerFor and I don't want the menu to open when clicking on the parent div. I only want the menu to open when clicking the small cogwheel button.

<gridster-item [item]="w" [class.editing]="w.editing" [matMenuTriggerFor]="appMenu">
  <div class="primary-header widget-settings-bar" color="primary" *ngIf="showSettingsBar == true">
    <div class="widget-settings-button contrast-color">
      <mat-icon class="icon" aria-hidden="false" aria-label="Delete">settings
      </mat-icon>
    </div>
    <div class="widget-settings-button contrast-color">
      <mat-icon class="icon" aria-hidden="false" aria-label="Duplicate" (click)="duplicateDashboardWidget(w)">
        control_point_duplicate</mat-icon>
    </div>
    <div class="widget-settings-button contrast-color">
      <mat-icon class="icon" aria-hidden="false" aria-label="Delete" (click)="deleteDashboardWidget(w)">delete
      </mat-icon>
    </div>
  </div>
  <div class="grid-widget mat-card2">
    <ndc-dynamic [ndcDynamicComponent]=components[w.component] [ndcDynamicInputs]="w"></ndc-dynamic>
  </div>
</gridster-item>
1

1 Answers

2
votes

Use ViewChildren to get a reference to all the matMenuTriggers.

@ViewChildren('menuTrigger') matMenuTriggers: QueryList<MatMenuTrigger>;

Then after page is loaded itterate through and disable the _handleClick

  public ngAfterViewInit() {
    this.matMenuTriggers.changes.subscribe((matMenuTriggers) => {
      matMenuTriggers.toArray().forEach((matMenuTrigger) => matMenuTrigger._handleClick = () => {});
    });
  }

You can still call openMenu programmatically to open and close the menu from somewhere else. It will open in relationship to the parent div avoiding overlap.

        <div class="widget-settings-button contrast-color" (click)="menuTrigger.openMenu()">
          <mat-icon class="icon" aria-hidden="false" aria-label="Delete">settings
          </mat-icon>
        </div>