Passing data to mat-menu

13,147

You can certainly pass an object to a mat-menu by using the matMenuTriggerData directive. This object can contain a single value, another object, or even an array of values or objects. Here's how I solved it:

My challenge was this: I wanted to dynamically build a list of menu items (mat-menu-item) based on the contents of an array. How did I manage to pass that array of objects to my mat-menu?

In your component class you can define the array of objects:

    export class MyComponent implements OnInit {
      menuData: any;

      ngOnInit() {
        this.menuData = {
          menuItems: [
            {code: '1', name: 'first'},
            {code: '2', name: 'second'}
          ]
        };
      }
    }

Notice that the object I will pass to the matMenuTriggerData directive of the button that opens the mat-menu content, is the data member called menuData. This member has only one property which is an array of objects. These represent the actual menu items I want to display in my template. The template is shown below:

    <mat-menu #app-menu="matMenu">
      <ng-template matMenuContent let-aliasMenuItems="menuItems">
        <button mat-menu-item *ngFor="let item of aliasMenuItems">
          Item {{item.code}}: {{item.name}}
        </button>
      </ng-template>
    </mat-menu>
    <button mat-icon-button [matMenuTriggerFor]="app-menu" [matMenuTriggerData]="menuData">
      <mat-icon>more_vert</mat-icon>
    </button>

Let me explain what is going on in the template: The button defined in the bottom of the template has been linked to the mat-menu called 'app-menu'. This is done by typing [matMenuTriggerFor]="app-menu".

The next thing we do is passing the component's member data to the mat-menu through this directive: [matMenuTriggerData]="menuData". The mat-menu instance that we named app-menu can now grab the content of that member data.

As you can see, the <ng-template> accesses the property of 'menuData' that we have named 'menuItems'. The <ng-template> adds a pointer or alias to that property (called aliasMenuItems), like this: <ng-template let-aliasMenuItems="menuItems">. Now we are able to loop through our defined array of menu items inside the <ng-template>.

In my example, I create a <button mat-menu-item></button> element for each menu item object that exists in my component's menuData.menuItems array, like this:

    <button mat-menu-item *ngFor="let item of aliasMenuItems">
      Item {{item.code}}: {{item.name}}
    </button>

I hope you find this answer useful.

Share:
13,147

Related videos on Youtube

Petr Averyanov
Author by

Petr Averyanov

Updated on September 16, 2022

Comments

  • Petr Averyanov
    Petr Averyanov over 1 year

    My 'awesome' menu:

    <mat-menu #appMenu="matMenu">
      <ng-template matMenuContent let-myobject="myobject">
        <button mat-menu-item>Delete {{myobject.name}}</button>
        <button mat-menu-item>Smth else</button>
      </ng-template>
    </mat-menu>
    
    <button mat-icon-button [matMenuTriggerFor]="appMenu" [matMenuTriggerData]="{myobject: myobject}">
       <mat-icon>more_vert</mat-icon>
    </button>
    

    First question is if it is ok? Wrote this following documentation but let-myobject="myobject" and {myobject: myobject} looks like overhead (?)

    Second question is if I want to calculate some data based on myobject - how I do that? I want it to be calculated just before menu is opened.

    [matMenuTriggerData]="getData(myobject)" - cant make this or similar work

    <ng-template matMenuContent let-data="getData(myobject)"> - cant make this or similar work either

    I know that I can replace ng-template with component here, but then for e.g. 10 menu items I will need to do 10 outputs in this component. (? or I cant...)

    • ashish.gd
      ashish.gd about 5 years
      Sometimes angular syntax does seem overhead :) However, you should avoid passing a method to any input properties as they will get called for every change detection affecting the performance. What you can do here is listen to the menuOpened event of the MatMenuTrigger directive and then calculate in the handler function and set the right matMenuTriggerData reference.
  • Ittit
    Ittit almost 5 years
    Just wanted to mention that you can also call a method on your component that returns an object containing the same array: [matMenuTriggerData]="getMenuData()".
  • Petr Averyanov
    Petr Averyanov almost 5 years
    Giving you credit for such long answer... But seems like you did not get my problem -- imagine you have 100 matMenuTriggerFor on page for same menu -- how to get this working without creating 100 objects for each?
  • Ittit
    Ittit almost 5 years
    So you want to re-use that single mat-menu element inside your template, and have it receive separate sets of data (menu items) from several different buttons? If you click on button 1, you want to use the matMenuTriggerData attribute to send one list of menu items to the mat-menu element, and by using button 2, you send a different list of menu items to it? In that case I would put the mat-menu inside a separate component which can receive an array of menu items via an @Input property. Then you could use it like this in your template: <my-menu [menuItems]="listOfItemsFromModel"></my-menu>.
  • Ittit
    Ittit almost 5 years
    Please let me know if you want me to show you an example of how to do what I suggest in my previous comment. I'll gladly update my suggestion above. :-)
  • Jun
    Jun about 3 years