julianobrasil
julianobrasil

Reputation: 9357

Angular Material 2 MatMenu - Dynamically creation

I need to dynamically create a bunch of MatMenu. So far I have no idea of how I can:

1 - Create a template reference variable dynamically (for the mat-menu component)

2 - Reference the dynamically created variable (to use in the [matMenuTriggerFor] attribute of the trigger)

I've searched a little and found nothing about it.

Does anyone manage do deal with it?

Upvotes: 18

Views: 25592

Answers (1)

julianobrasil
julianobrasil

Reputation: 9357

Solved by encapsulating the MatMenu and the associated trigger in an Angular custom component.

Scenario: There's an array of objects that must be shown in a table. The last column of the table must show the MatMenu with two options: Edit and Delete.

So I made a custom angular component containing just the MatMenu and its trigger inside it. This component uses two-way data bind to receives the object that must be edited/deleted and proceed with the desired action:

  • If the purpose is to edit, shows a dialog to edit the data

  • If the purpose is to delete, it deletes the data

Once the action is completed, the host of the custom component must perform one of two actions:

1 - update the array replacing/deleting the edited/removed element

2 - update the entire array, asking for all its forming data again from the server

In both cases, you'll have to monitor the changes and do any updates in the array whenever the update/deletion is finished. An alternative is to create another two-way data bound variable and pass the entire array as a two-way data-bound parameter to the custom component.

I built up this Stackblitz to better show it: https://stackblitz.com/edit/repeating-menu-approach-1

<table>
  <tr *ngFor="let el of [1,2,3,4,5]">
    <td>Text line of menu {{el}}</td>
    <td style="width: 5%">
      <menu-overview-example [x]="el" (clickedItem)="hostClickHandler($event)"></menu-overview-example>
    </td>
  </tr>
</table>

Another approach

There's one more way to do it: you can use <ng-container> and it will just create a scope for your template variable (https://stackblitz.com/edit/repeating-menu-approach-2):**

<table>
  <ng-container *ngFor="let el of [1,2,3,4,5]">
    <tr>
      <td>Text line of menu {{el}}</td>
      <td style="width: 5%">
        <button mat-icon-button [matMenuTriggerFor]="menu">
          <mat-icon>more_vert</mat-icon>
        </button>
        <mat-menu #menu="matMenu">
          <button mat-menu-item (click)="clickHandler('Item 1 of Menu ' + el)">Menu {{el}}:Item 1</button>
          <button mat-menu-item (click)="clickHandler('Item 2 of Menu ' + el)">Menu {{el}}:Item 2</button>
        </mat-menu>
      </td>
    </tr>
  </ng-container>
</table>

Interestingly, each #menu template variable above will be unique, fully isolated from the others #menu template variables created by the *ngFor loop.

[UPDATE]: As a matter of fact, you can drop the <ng-container> and use the *ngFor directly on the <tr> element along with the #menu template variable. The isolated scope will be created in the same way as described above. But as I think it's more comprehensible for angular new users the presence of ng-container, I decided to keep it on this answer.

Upvotes: 32

Related Questions