Every application needs to have some sort of navigation, to provide users with a better experience.
Creating a navigation menu will be our goal in this article.
We have to use angular routing as well, and we are going to do that, but we won’t dive deep inside the routing concepts. If you want to learn in more detail about the angular routing, you can read Angular Navigation And Routing.
Because this series is all about angular material, this article won’t be an exception. We will focus on creating a navigation menu by using different material components. Once we are done, we will have a fully responsive and functional navigation menu with the routing logic to support the complete process.
VIDEO: Angular Material Complete Responsive Navigation video.
For the complete navigation and all the basic instructions of the Angular Material series, check out: Introduction of the Angular Material series.
We strongly recommend reading our Angular Series prior to reading this article if you want to restore your knowledge about that topic or to learn Angular development overall.
The source code is available at GitHub Angular Material Navigation Menu – Source Code
We are going to divide this post into several sections:
- Creating Routes
- Angular Material Navigation Development
- Creating Navigation Header
- Creating Side-Navigation
- Multi-Menu in Side-Nav
- Conclusion
Creating Routes
Let’s start with creating a new routing module:
ng g module routing --module app
A next step is to modify the routing.module.ts
file:
import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { Routes, RouterModule } from '@angular/router'; import { HomeComponent } from '../home/home.component'; const routes: Routes = [ { path: 'home', component: HomeComponent}, { path: '', redirectTo: '/home', pathMatch: 'full' } ]; @NgModule({ imports: [ CommonModule, RouterModule.forRoot(routes) ], exports: [ RouterModule ], declarations: [] }) export class RoutingModule { }
Finally, let’s modify the
app.compnent.html
file to complete the routing part for now:<app-layout> <main> <router-outlet></router-outlet> </main> </app-layout>
We should be able to see our home component again, but this time it is served on the
/home
route.Angular Material Navigation Development
Angular Material provides different components that we can use to create nicely styled, responsive, and effective navigation in our app. But we need to start with something, don’t we? So, let’s start with the app.component.html
file modification by using the mat-sidenav-container
component:
<app-layout> <mat-sidenav-container> <mat-sidenav #sidenav role="navigation"> <!--this is a place for us to add side-nav code--> </mat-sidenav> <mat-sidenav-content> <!--in here all the content must reside. We will add a navigation header as well--> <main> <router-outlet></router-outlet> </main> </mat-sidenav-content> </mat-sidenav-container> </app-layout>
We create a container for a side navigation bar and specify the part for our content. As you can see the
<mat-sidenav>
element defines a place for a side navigation and the <mat-sidenav-content>
element defines a place for our content. We need to use the local reference #sidenav
, and a little bit later, you will see why.
Of course, this won’t work. We need to register the module in the material.module.ts
file:
import { MatSidenavModule } from '@angular/material/sidenav'; @NgModule({ imports: [ CommonModule, MatTabsModule, MatSidenavModule ], exports: [ MatTabsModule, MatSidenavModule ],
Now, we should have a working application again with some grayish background. Let’s style this a bit in the
app.component.css
file:mat-sidenav-container, mat-sidenav-content, mat-sidenav { height: 100%; } mat-sidenav { width: 250px; } main { padding: 10px; }
And let’s modify the
styles.css
file:/* for sidenav to take a whole page */ html, body { margin: 0; height: 100%; }
That is it. We have all prepared and it is time to start working on our navigation header component.
Creating Navigation Header
To create a navigation header, we need to use the mat-toolbar
element. But first thing first.
This component has its own module, so we need to register that module inside the material.module.ts
file:
import { MatToolbarModule } from '@angular/material/toolbar';
imports: [ MatToolbarModule,
exports: [ MatToolbarModule,
After that, we are going to create a new header component:
ng g component navigation/header --skipTests
Now it is time to include this component inside the
app.component.html
file, right above the <main>
tag:<mat-sidenav-content> <app-header></app-header> <main> <router-outlet></router-outlet> </main> </mat-sidenav-content>
Then, let’s modify the
header.component.html
file:<mat-toolbar color="primary"> <div fxHide.gt-xs> <button mat-icon-button (click)="onToggleSidenav()"> <mat-icon>menu</mat-icon> </button> </div> <div> <a routerLink="/home">Owner-Account</a> </div> <div fxFlex fxLayout fxLayoutAlign="end" fxHide.xs> <ul fxLayout fxLayoutGap="15px" class="navigation-items"> <li> <a routerLink="/owner">Owner Actions</a> </li> <li> <a routerLink="/account">Account Actions</a> </li> </ul> </div> </mat-toolbar>
Basically, we create our navigation with the menu icon (we still need to register its own module), and the Owner-Account part that navigates to the home component. As you can see, we use the
fxHide.gt-xs
directive, which states that this part should be hidden only on the screen that is greater than the extra small.
We have another part of a navigation which is positioned on the end of the navbar and hidden only for the extra small screen.
To continue, let’s register the MatIconModule
and MatButtonModule
inside the material module file:
import { MatIconModule } from '@angular/material/icon'; import { MatButtonModule } from '@angular/material/button';
imports: [ MatButtonModule, MatIconModule,
exports: [ MatButtonModule, MatIconModule,
Before we start the application, let’s just add the
onToggleSideNav
function in the header.component.ts
file:
public onToggleSidenav = () => { }
Right now, our menu looks like this:
Looking beautiful right? 😀 😀
Of course not, but we have the starting functionality in place and we are going to make it much nicer.
To do that, let’s modify the header.component.css
file:
a { text-decoration: none; color: white; } a:hover, a:active{ color: lightgray; } .navigation-items{ list-style-type: none; padding: 0; margin: 0; } mat-toolbar{ border-radius: 3px; } @media(max-width: 959px){ mat-toolbar{ border-radius: 0px; } }
Now if we can have another look at our menu. It looks much nicer, isn’t it?
If we take a look at the icon button code, we can see the onToggleSidenav()
event. We need to implement it inside the header.component.ts
file:
import { Component, OnInit, Output, EventEmitter } from '@angular/core'; @Component({ selector: 'app-header', templateUrl: './header.component.html', styleUrls: ['./header.component.css'] }) export class HeaderComponent implements OnInit { @Output() public sidenavToggle = new EventEmitter(); constructor() { } ngOnInit() { } public onToggleSidenav = () => { this.sidenavToggle.emit(); } }
If you want to learn more about
@Output
directives, you can read Angular Series Article About Decorators.
Finally, we have to react to this event emitter inside our app.component.html
file:
<mat-sidenav-content> <app-header (sidenavToggle)="sidenav.toggle()"></app-header> <main> <router-outlet></router-outlet> </main> </mat-sidenav-content>
Now it is obvious why we need the
#sidenav
local reference inside the mat-sidenav
component.
Our result should look like this:
Excellent. The time has come to implement the side navigation.
Creating Side Navigation
To create side navigation items, we are going to use the mat-nav-list
element that resides inside MatListModule
. So, let’s register this module first in the material.module.ts
file:
import { MatListModule } from '@angular/material/list';
imports: [ MatListModule,
exports: [ MatListModule,
Then let’s create the
sidenav-list
component and modify the sidenav-list.component.html
file:ng g component navigation/sidenav-list --skipTests
<mat-nav-list> <a mat-list-item routerLink="/home" (click)="onSidenavClose()"> <mat-icon>home</mat-icon> <span class="nav-caption">Home</span> </a> <a mat-list-item routerLink="/owner" (click)="onSidenavClose()"> <mat-icon>assignment_ind</mat-icon> <span class="nav-caption">Owner Actions</span> </a> <a mat-list-item routerLink="#" (click)="onSidenavClose()"> <mat-icon>account_balance</mat-icon><span class="nav-caption">Account Actions</span> </a> </mat-nav-list>
As you can see, we use the
mat-nav-list
as a container with all the anchor tags containing the mat-list-item
attributes. The click event is there for every link, to close the side-nav as soon as a user clicks on it. Finally, every link contains its own mat-icon.
Let’s continue by adding some styles to the sidenav-list.component.css
file:
a{ text-decoration: none; color: white; } a:hover, a:active{ color: lightgray; } .nav-caption{ display: inline-block; padding-left: 6px; }
And finally, let’s modify the
sidenav-list.component.ts
file:import { Component, OnInit, Output, EventEmitter } from '@angular/core'; @Component({ selector: 'app-sidenav-list', templateUrl: './sidenav-list.component.html', styleUrls: ['./sidenav-list.component.css'] }) export class SidenavListComponent implements OnInit { @Output() sidenavClose = new EventEmitter(); constructor() { } ngOnInit() { } public onSidenavClose = () => { this.sidenavClose.emit(); } }
That’s it. We can now open the
app.component.html
file and modify it to add the side-nav component:<mat-sidenav #sidenav role="navigation"> <app-sidenav-list (sidenavClose)="sidenav.close()"></app-sidenav-list> </mat-sidenav>
In this code, we react to the event emitter from the sidenav-list component and close the side-nav by using the
#sidenav
local reference.
Now, all we have to do is to take a look at our result:
Multi-Menu in Side-Nav
There is one more thing we want to show you. For now, we only have a one clickable link per section, inside our sidenav. But what if we want to have a menu item and when we click that menu item other options appear? Well, we are going to show you how to do that as well.
So, in the sidenav-list.component.html
file, we need to add the following code below the last anchor tag:
<mat-list-item [matMenuTriggerFor]="menu"> <mat-icon>unfold_more</mat-icon> <a matline>Example</a> </mat-list-item> <mat-menu #menu="matMenu"> <button mat-menu-item (click)="onSidenavClose()">View profile</button> <button mat-menu-item (click)="onSidenavClose()">Add contact</button> </mat-menu>
For this to work, we need to register
MatMenuModule
:import { MatMenuModule } from '@angular/material/menu';
imports: [ MatMenuModule,
exports: [ MatMenuModule,
As a result, we should have a multi-menu option in our side navigation bar:
Conclusion
Awesome.
Now we have our own responsive navigation menu, built from scratch.
In this article we have learned:
- How to create the Angular Material Navigation
- The way to create a side navigation material menu
- How to implement multi-menu options in the side navigation
In the next article, we are going to learn more about Material Table with Filter, Sort and Paging functionalities.
Hi,
Which is the better, Bootstrap or Material?
Which one is better to use?
Or maybe both at the same time?
Can you give me some personal experiences?
Hello Boldo. Well, I am not an experienced Front-End developer to say which one is better, I enjoyed using both in separate projects. But using both at the same time, I can say – a big NO – I can say this for sure. If you use Material you don’t need Bootstrap at all. There exists a material design for bootstrap, bur for me, it makes no sense. Material is pretty enough on it self, and you can use flex, as I did in this series, to organize the elements on the page.
Свака част мајсторе! 🙂
Да си ми жив и здрав.
Hvala puno. Drago mi je da je i dalje korisno iako je starija verzija i Angulara i Material-a.
https://github.com/angular/flex-layout
Import it to the project guys, it’s missing in the guide.
Since this is the second article of the angular material series, we introduced flex-layout in the previous article.
I did everything as according to your code, but design breaks for some reason. Can you look at the screenshot and try to identify what may have gone wrong with my implementation. my package versions are as below:
“dependencies”: {
“@angular/animations”: “~13.1.0”,
“@angular/cdk”: “^13.1.2”,
“@angular/common”: “~13.1.0”,
“@angular/compiler”: “~13.1.0”,
“@angular/core”: “~13.1.0”,
“@angular/flex-layout”: “^13.0.0-beta.38”,
“@angular/forms”: “~13.1.0”,
“@angular/material”: “^13.1.2”,
“@angular/platform-browser”: “~13.1.0”,
“@angular/platform-browser-dynamic”: “~13.1.0”,
“@angular/router”: “~13.1.0”,
“hammerjs”: “^2.0.8”,
“rxjs”: “~7.4.0”,
“tslib”: “^2.3.0”,
“zone.js”: “~0.11.4”
},
Well, I can’t be sure. The one obvious difference is the framework version you are using. Yours is 13. But, have you tried downloading our source code and inspecting it?
Thank you for replying so fast! I checked again and found FlexLayoutModule was not imported in my SharedModule. Issue resolved!
I have inserted a logo far left, in the middle will be my menus, and far right I will have a dropdown for Profile and Logout buttons. How do you think that will work?
Thank you!
I see no issue with that, you will just have to play a bit with Angular Flex I think to do that.
Hello Marinko,
This is an excellent article. I have retrofitted the code into an application I am working on (itself still very much your “Angular Authentication Actions with IdentityServer4”) and I have a couple of oddities, one is just a css glitch, but the other is unusual. The words are appearing instead of the menu icons. I’ve been up and down the code a few times and I cant spot what I have left out.
Regards
Ger
I left this out of the original post
Found it – I missed the changes to Index.html
I’m glad you solved it, Gerard.
What if i don’t want to show the header (for example on /login routing) In this way theoretically i can’t hide the navbar since the router outlet is on mat-sidenav-content, right?
Hi Gabe. Let me say I didn’t try it, but I am not sure why woudn’t you be able to hide the navigation-header? The rouer-outlet will just display the content of the required route, but the navigation-header is another component that you can conditionaly render on the page.
This is a really great series!! I am wondering can this be made full width?
If you refer to mat side nav, you can always try to increase width in the css. It already has some default width.
The multi menu side nav is not working by just importing the code. Can someone please tell the fix for it. Its urgent
Hi Vishal. I am not sure what’s not working. This entire series was updated yesterday to Angular 9 and a newest Material version. All the imports have been fixed. I just tested again our code and everything works. What is exactly your problem? It is not enough to just import the MatMenuModule, you also have to modify the sidenav-list.component.html file with additional mat-list-item, mat-menu- and two mat-menu-items.
What is app-layout
Hello Gyozo. This article is the second part of the Angular Material series, so it is normal that some things already exist. That’s the case with the AppLayout component, which was created in the previous part. So please read the previous part for a full explanation, or read entire series to see the full story. Best regards.
Uncaught (in promise): TypeError: Cannot read property ‘runOutsideAngular’ of undefined.can u tell me solution
Hello, Marinko. One more question: how to stay on the current page when refreshing the browser? All the time it navigates to the login page
You should modify your auth guard for sure. I believe you probably have one for the authentication. I don’t know your code, but I assume that in your guard you are navigating to the login page if some condition isn’t fulfiled. Try to modify that validation.
you’re right, I use it. I can’t catch the point where the auth guard redirects the path after refreshing
The canActivate is involved only after UI activity. And more, I removed canActivate from then route, but nothing changed
Hello Marinko,
Thank a lot. This tutorial is super helpful.
Quick question, is Navigation Header still working with angular 8? I download code from github, however it became blank like the attached picture. Any suggest?
https://uploads.disquscdn.com/images/b252973eff3bd5f57e0f5f0951cd9a6536f53d7651096a322f85faea3bbf12a3.png
Hello Kyle. If you downloaded our source code, than it has nothing to do with angular 8 because it was not built in that version. It is built in Angular v6. As much as I can see from your picture, complete material is not working for you because the Tab component is not working as well. The source code must work, there is no question about it (just checked it), but I’m not sure why you have such a problem. Have you checked your chrome console, maybe something can be found in there?
Hey. You are awesome. Thank for a quick respond.
It’s happen when we disable @import indigo-pink ( one of 4 build in angular themes) in the style.css
Your code are working well. however, if I clone from git , it’s missing @import themes
I fixed it by added the indigo-pink.
Again, your tutorial is awesome man.
Edit: I’m using your code with Angular 8. Still fine. Just let you know in case someone want to know if it’s working @V8 or not.
Thank you for the comments and I’m glad you are finding these series useful. Again it is strange that the @import statement is not in the CSS file, because it is just a simple line of code, and I just checked it is in a repository code, very strange. Again, thanks a lot mate for the comments and all the nice words. Best regards.
Hello, Marinko. Thank you for another great Angular tutorial!
I need the first page to be the login page and there should be nothing but the login form on it. The navbar should appear only after successful login. How can this be done or where to read about it?
Hello Vasily. In your auth component or auth service (what ever you like) create a public property which will tell you wether the user is authenticated or not. Then create your navigational menu in a separate component but render it conditionaly. So if your auth property is true (means user is logged in) than you can render your menu, if it is false than the menu won’t be rendered. Just like that 😀
Hello, Marinko. Thank you again for your tutorials And thank you very much for your hint on how to hide/show navbar depending on authorization. I did it! As my project progresses, I have new questions that may already be considered in your tutorials or books. If so, please tell me where I can read the following topics: formatting rows and cells of the table depending on the value of the fields when filling in the table and the dynamic formation of the layout elements (menu, buttons)
Thanks as lot!
To set your css formating dinamically, you should use [ngClass] attribute. It allows you to specify json object as a value which holds name of the classes and true or false if you want specific class to be implemented. So if the value of your cell is greater than one it can be colored in red otherwise it should be blue (you get the point). For [ngClass] value you can provide a function that will do the checks for you and return a json object with required class to be applied. Just search ngClass in angular and you will find a lot of information on Google 😀 Hope this will help you.
Ok, thanks!
To set your css formating dinamically, you should use [ngClass] attribute. It allows you to specify json object as a value which holds name of the classes and true or false if you want specific class to be implemented. So if the value of your cell is greater than one it can be colored in red otherwise it should be blue (you get the point). For [ngClass] value you can provide a function that will do the checks for you and return a json object with required class to be applied. Just search ngClass in angular and you will find a lot of information on Google 😀
I just followed this tutorial but my burger icon isn’t there for me to select and see the sidenav. I then downloaded your project from GitHub to try find where i went wrong and it isn’t on there either?
Hello Emma. Well, all I can say is that the burger icon is there, otherwise I would be lying to you guys that this is the way to create a navigation menu when it is not. I just checked again my project and source code and everything works well. So, I have to ask this question: Have you tried to shrink your page width below the 600 pixels? As you can see from the GIF file in the Create Side-Navigation section, your screen width must be below the 600px for the icon to show. Furthermore, this directive: “fxHide.gt-xs” states that the icon should be hidden on the greater than extra small screens.
Yeah I had it on my Mac and could not work out why it wasn’t showing. Thank you for this. The tutorial is great so thank you very much for that 🙂
You are very welcome. Thank you for reading it. Best regards.
Great tutorial! Thank you m8!
You are welcome Ruslan. I am so glad you like it and find it useful. Best regards.
I am getting a error saying “cannot find module ‘./home/home.component’ ” in routing.module.js
1: CHeck if your home component is exit or not. If not, create one and setup path in routing.module.ts.
2: CHeck if it created but you did not import it.
You need to fix this: in home.component.html. It breaks everything… At least when using newer Angular…
Making it:
is probably what you wanted–At least I think your wrap meant stretching content over the whole “column” (as seen from the animation). Anyway, using it this way makes the child elements stretch to fill up whole horizontal space of its parent.
Hello, here is your solution. For the Welcome part next to the fxFlexAlign=”center” add aditional fxFlexAlign.xs=”start” this will move the Welcome part to the left when screen gets extra small. For the tab fix create this in your main styles.css file:
.mat-tab-label .mat-tab-label-content{
white-space: pre-wrap !important;
}
It will override the white-space property, which is initially set to no-wrap. Once you set this you will get this result:
https://uploads.disquscdn.com/images/c3aa0205125b8c5ad967d6d2526309adbe452d14917dbf58e4b51d5f608d576b.png
That is all it takes. All the best.
My edited answer solved this in a far simpler way. Removing the “wrap” from the `section`, and adding ` fxlayoutalign=”stretch”` property to it instead makes it work just as intended. And it also causes the `tabs` from `material` to function as intended, meaning create the arrows for switching between them when they get too small (like in the animation here).
Too bad I saw your answer only once I already solved the problem on my own, after properly researching FlexLayout from angular.
Thank you. I will try your solution as well. One more time thank you a lot for the suggestion.
Glad to be of help. Your tutorial helped me far more than that fix of mine will ever help you. 😛
Was looking for a way to replace Bootstrap when I stumbled upon this tutorial, where I found you using FlexLayout–it’is just what I was looking for.
Thank you, I searched a lot for a tutorial like this! On firefox the tab are not responsive, on chrome you have left/right arrow popping up, and nothing on firefox, any idea why?