Angular Material Menu
In this article we'll talk about angular material menus, basically menu displays a list of choices on a temporary surface the choices appear when user interact with the button action or some other type of control.
Importing the module
Now as always the first step is to import the appropriate module so in material.module.ts
I'm gonna import MatMenuModule
from @angular/material
and add it to the MaterialComponents
array.
import { MatMenuModule } from "@angular/material";
const MaterialComponents = [
MatMenuModule
];
Use MatMenuModule in HTML
Let's open app.component.html
file and use <mat-menu>
component, to specify the choices for the menu we use the button elements with a mat-menu-item
attribute <button mat-menu-item>
.
<mat-menu>
<button mat-menu-item>Angular</button>
<button mat-menu-item>ReactJs</button>
<button mat-menu-item>VueJs</button>
</mat-menu>
If you save this and take a look at the browser you'll not going to see anything that is because by itself the Mat Menu elements does not render anything it has to be triggered from a UI element or programmatically so let's add an element to open the menu I'm gonna add a new button element.
<mat-menu>
<button mat-menu-item>Angular</button>
<button mat-menu-item>ReactJs</button>
<button mat-menu-item>VueJs</button>
</mat-menu>
<button mat-flat-button>Menu</button>
Now that we have the button element we need to associate the menu with this button element and there is two steps process.
- First Step: On the
<mat-menu>
component create a template reference variable and assign to it thematMenu
directive like this<mat-menu #appMenu="matMenu">
with#appMenu
is the name of our template reference variable. - Second Step: link the button with our template reference variable using the
matMenuTriggerFor
directive to our template reference variable<button mat-flat-button [matMenuTriggerFor]="appMenu">Menu</button>
so in simple English we told the button that there is a menu called appMenu which can be triggered by clicking on you (button).
<mat-menu #appMenu="matMenu">
<button mat-menu-item>Angular</button>
<button mat-menu-item>ReactJs</button>
<button mat-menu-item>VueJs</button>
</mat-menu>
<button mat-flat-button [matMenuTriggerFor]="appMenu">Menu</button>
Now if you save the file and take a look again at browser we have the menu button click on the menu and the menu with choices appears.
If you have that big space between choices that's because of the margin that we had added earlier in
app.component.scss
remove it and you'll fix that.
button {
margin: 0rem;
}
So this is how you create a basic menu with angular material. Next let's see how to create a nested menu.
Nested menu in angular material
The first step is to create the sub menu and we're going to repeat the process of creating a menu and attaching a template reference variable so inside <mat-menu>
I'm gonna add 3 menu items like this.
<mat-menu>
<button mat-menu-item>Angular 2</button>
<button mat-menu-item>Angular 4</button>
<button mat-menu-item>Angular 7</button>
</mat-menu>
on the <mat-menu>
compmonent I'm gonna add the reference variable which is going to be called subMenu and we assign to it a matMenu directive <mat-menu #subMenu="matMenu">
.
<mat-menu #subMenu="matMenu">
<button mat-menu-item>Angular 2</button>
<button mat-menu-item>Angular 4</button>
<button mat-menu-item>Angular 7</button>
</mat-menu>
This is a submenu for Angular choice in main menu so all we have to do is add the trigger directive again but this time on the first item in the main menu which is Angular.
<mat-menu #appMenu="matMenu">
<button mat-menu-item [matMenuTriggerFor]="subMenu">Angular</button>
<button mat-menu-item>ReactJs</button>
<button mat-menu-item>VueJs</button>
</mat-menu>
In simple English this Angular Choice is going to trigger this submenu if we save this final code and take a look at the browser.
<mat-menu #appMenu="matMenu">
<button mat-menu-item [matMenuTriggerFor]="subMenu">Angular</button>
<button mat-menu-item>ReactJs</button>
<button mat-menu-item>VueJs</button>
</mat-menu>
<mat-menu #subMenu="matMenu">
<button mat-menu-item>Angular 2</button>
<button mat-menu-item>Angular 4</button>
<button mat-menu-item>Angular 8</button>
</mat-menu>
<button mat-flat-button [matMenuTriggerFor]="appMenu">Menu</button>
You can see that we have a nested menu you don't have to click on Angular when you hover on the choice the submenu of angular is opened.
Positioning the menu
To place the menu wherever we want you have xPosition and yPosition attributes so on the <mat-menu>
component we can specify our xPosition
and yPosition
, for xPosition
can take the values of before or after and yPosition
can take the values of above or bellow. Let's go those values here bellow.
<mat-menu #appMenu="matMenu" xPosition="after" yPosition="above">
<button mat-menu-item [matMenuTriggerFor]="subMenu">Angular</button>
<button mat-menu-item>ReactJs</button>
<button mat-menu-item>VueJs</button>
</mat-menu>
<mat-menu #subMenu="matMenu">
<button mat-menu-item>Angular 2</button>
<button mat-menu-item>Angular 4</button>
<button mat-menu-item>Angular 8</button>
</mat-menu>
<button mat-flat-button [matMenuTriggerFor]="appMenu">Menu</button>
If you save a take a look at the browser you'll see something like this.
You can see that the menu opens to the right so
xPosition
is working fine but the menu still opens bellow the button which is not what we specify with yPosition="above"
which means it should opens above the button and that is simply because there is no space available at the top to fix that let's reposition the button by adding this css to app.component.scss
file.
.menu-button{
margin: 10rem;
}
back to the app.component.html
let's add the .menu-button
class to the button element.
<button class="menu-button" mat-flat-button [matMenuTriggerFor]="appMenu">Menu</button>
Now if we go back to the browser and click on the button element you can see that the menu opens to the right which is xPosition="after"
and to the top which is yPosition="above"
.
That how we control the position of the menu using those attributes with values of after/before for
xPosition
and bellow/above for yPosition
.
Lazy rendering
By default the menu choices are initialized even before the menu is opened that is even before clicking on the button element the choices have already been loaded for the menu. We can differ initialization until the menu is open using the <ng-template>
directive and mat-menu-content
attribute, this allows us to have dynamic content as many choices let's see how?
I'm gonna create a new menu with a reference variable called #lazyMenu and set it to matMenu within the menu component we are going to use the ng-template
directive don't forget to add the attribute matMenuContent
on this directive and within this directive we specify the menu items.
<mat-menu #lazyMenu="matMenu">
<ng-template matMenuContent>
<button mat-menu-item>Settings</button>
<button mat-menu-item>Log Out</button>
</ng-template>
</mat-menu>
Let's create two trigger buttons to this same menu.
<mat-menu #lazyMenu="matMenu">
<ng-template matMenuContent>
<button mat-menu-item>Settings</button>
<button mat-menu-item>Log Out</button>
</ng-template>
</mat-menu>
<button mat-button [matMenuTriggerFor]="lazyMenu">Rb Hosh</button>
<button mat-button [matMenuTriggerFor]="lazyMenu">HoshCoding</button>
Save the file and you see that when I click on each button the same menu appears and opened which is normal because we link the two buttons to the same menu by matMenuTriggerFor
attribute.
But what I want is when I click on Rb Hosh button I need the choice to be Log Out Rb Hosh, the same when I click on HoshCoding I want the menu choice to be Log Out HoshCoding, basically we're looking at dynamic choices for that we make use of matMenuTriggerData
attribute so back in app.component.html
on the trigger buttons we'll add an extra attribute which is matMenuTriggerData
which take an object as a value.
<button
mat-button
[matMenuTriggerData]="{ name: 'Rb Hosh' }"
[matMenuTriggerFor]="lazyMenu"
>
Rb Hosh
</button>
<button
mat-button
[matMenuTriggerData]="{ name: 'HoshCoding' }"
[matMenuTriggerFor]="lazyMenu"
>
HoshCoding
</button>
Now each button has it's own data which in this case just a simple name. So after specifying the data let's create a local variable on ng-template
tag and retrieve the name using interpolation.
<mat-menu #lazyMenu="matMenu">
<ng-template matMenuContent let-name="name">
<button mat-menu-item>Settings</button>
<button mat-menu-item>Log Out {{ name }}</button>
</ng-template>
</mat-menu>
As you can see if you click on Rb Hosh it says Log Out Rb Hosh and if you click on HoshCoding button it says Log Out HoshCoding.
So what is happening is on the trigger button we specify the [matMenuTriggerData]
and we specify the name so we are saying:
hey I know that this button is a trigger for a menu but when I'm triggering that menu let me pass some additional information that the menu can use to dynamically render some data.
In the <mat-menu>
component we use <ng-template>
tag and on the <ng-template>
tag we specify the let-name="name"
variable that we want to access and that can be interpolated in the button element choice.
Let's add one more property to understand how it works, to matMenuTriggerData
I'm gonna add a new property called hobby for Rb Hosh the hobby is football and for HoshCoding the hobby is teaching, Now in the menu on the ng-template
tag we declare another variable let-hobby="hobby"
and let's add a new menu choice and bind the hobby
.
<mat-menu #lazyMenu="matMenu">
<ng-template matMenuContent let-name="name" let-hobby="hobby">
<button mat-menu-item>Settings</button>
<button mat-menu-item>Hobby is {{ hobby }}</button>
<button mat-menu-item>Log Out {{ name }}</button>
</ng-template>
</mat-menu>
<button
mat-button
[matMenuTriggerData]="{ name: 'Rb Hosh', hobby: 'football' }"
[matMenuTriggerFor]="lazyMenu"
>
Rb Hosh
</button>
<button
mat-button
[matMenuTriggerData]="{ name: 'HoshCoding', hobby: 'teaching' }"
[matMenuTriggerFor]="lazyMenu"
>
HoshCoding
</button>
As you can see when you click on Rb hosh it says Hobby is football and when you click on HoshCoding it says Hobby is teaching, it the same menu but with dynamic content.
Alright that is all about navigation and menus in angular material if you want more examples let me point to documentation using this link.
-
1. Angular Material - Getting Started
-
2. Angular Material - Material Module
-
3. Angular Material - Typography
-
4. Angular Material - Buttons
-
5. Angular Material Button Toggle
-
6. Angular Material Icons
-
7. Angular Material Badges
-
8. Angular Material Progress Spinner
-
9. Angular Material Navbar
-
10. Angular Material Sidenav
-
11. Angular Material Menu
-
12. Angular Material List
-
13. Angular Material Grid List
-
14. Angular Material Expansion Panel
-
15. Angular Material Cards
-
16. Angular Material Tabs
-
17. Angular Material Stepper
-
18. Angular Material Input
-
19. Angular Material Select
-
20. Angular Material Autocomplete
-
21. Angular Material Checkbox & Radio Button
-
22. Angular Material Date Picker
-
23. Angular Material Tooltip
-
24. Angular Material Snackbar
-
25. Angular Material Dialog
-
26. Angular Material Data Table
-
27. Angular Material Exploring Data Table
-
28. Angular Material Data table Filtering
-
29. Angular Material Data Table Sorting
-
30. Angular Material Data Table Pagination
-
31. Angular Material Virtual Scrolling