26

I have created a toolbar using Angular Material. However, it is not responsive. How can I make the toolbar responsive?

Code for toolbar:

<md-toolbar color = "primary">
    <button md-button class="md-primary" [routerLink]="['/basic']"><md-icon class = "icon-20">home</md-icon> &nbsp;Angular Concepts</button>
    <button md-button [mdMenuTriggerFor]="bMenu">Basic Concepts</button>
    <md-menu #bMenu="mdMenu">
      <button md-menu-item [routerLink]="['/a4']">Angular Component</button>
      <button md-menu-item [routerLink]="['/cli ']">Angular CLI</button>
      <button md-menu-item [routerLink]="['/inout']">Event Emitters</button>
      <button md-menu-item [routerLink]="['/template']">Template Driven Forms</button>
      <button md-menu-item [routerLink]="['/reactive']">Data Driven Forms</button>
      <button md-menu-item [routerLink]="['/directives']">Angular Custom Directives</button>
      <button md-menu-item [routerLink]="['/pipes']">Custom Pipes</button>
      <button md-menu-item [routerLink]="['/viewchild']">View Child</button>
      <button md-menu-item [routerLink]="['/view']">View Encapsulation</button>
    </md-menu>

    <button md-button [mdMenuTriggerFor]="aMenu">Advanced Concepts</button>
    <md-menu #aMenu="mdMenu">
      <button md-menu-item [routerLink]="['/ngrx']">Angular Redux using ngrx/store</button>
      <button md-menu-item [routerLink]="['/guard']">Angular Guards</button>
      <button md-menu-item [routerLink]="['/host']">Host & Host-Context</button>
    </md-menu>

    <button md-button (click)="openDialog()">&nbsp;Contact Card</button>
  </md-toolbar>
Edric
  • 24,639
  • 13
  • 81
  • 91
INFOSYS
  • 1,465
  • 9
  • 23
  • 50

5 Answers5

53

Here is my favorite way of creating a responsive Navigation Bar in Angular. If you use Angular 6, make sure you use a version 6.1+
Working example on Stackblitz: https://stackblitz.com/edit/angular-v6xwte

Example on a smaller screen: Toolbar on a small screen

Example on a larger screen: Toolbar on a large screen

Here are precise steps how to do it:

1) Install necessary packages. Type in your terminal:

npm install --save @angular/material @angular/cdk @angular/animations

npm install @angular/flex-layout --save

2) Import necessary modules in your app.module.ts

import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { FlexLayoutModule } from '@angular/flex-layout';
import {
  MatIconModule, MatButtonModule, MatSidenavModule, MatToolbarModule
} from '@angular/material';

Remember to add these modules to the imports array below.

3) Add Material Icons link to your index.html
The link must go before any Angular content.

<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">

4) In your styles.css add an Angular theme and set margins to 0%

@import "~@angular/material/prebuilt-themes/indigo-pink.css";
body{
    margin: 0%;
}

5) Add toolbar HTML code in your app.component.html

<div> 
  <mat-toolbar color="primary">
    <div fxShow="true" fxHide.gt-sm="true">
      <button mat-icon-button (click)="sidenav.toggle()">
        <mat-icon>menu</mat-icon>
      </button>
    </div>

    <a mat-button class="companyName" routerLink="/">
      <span>Site name</span>
    </a>
    <span class="example-spacer"></span>
    <div fxShow="true" fxHide.lt-md="true">
      <a mat-button routerLink="/about-us">About us</a>
      <a mat-button routerLink="/prices">Prices</a>
      <a mat-button routerLink="/start-page">Start page</a>
      <a mat-button routerLink="/offer">Offer</a>
      <a mat-button routerLink="/contact">Contact</a>
    </div>

  </mat-toolbar>
  <mat-sidenav-container fxFlexFill class="example-container">

    <mat-sidenav color="primary" #sidenav fxLayout="column" mode="over"  opened="false" fxHide.gt-sm="true">
      <div fxLayout="column">
        <a mat-button routerLink="/about-us">About us</a>
        <a mat-button routerLink="/prices">Prices</a>
        <a mat-button routerLink="/start-page">Start page</a>
        <a mat-button routerLink="/offer">Offer</a>
        <a mat-button routerLink="/contact">Contact</a>
      </div>
    </mat-sidenav>
    <mat-sidenav-content fxFlexFill>
      Awesome content
    </mat-sidenav-content>
  </mat-sidenav-container>
</div>

6) Style the toolbar in your app.component.css

.companyName{
    font-size: 150%;
}

.mat-toolbar{
  height: 7vh;
}

div {
    overflow: inherit;
}

.mat-sidenav-container{
  background-color: lightskyblue;
  min-height: 93vh !important;
}

a{
    text-decoration: none;
    font-size: 110%;
    white-space: normal;
}

button{
    font-size: 110%;
    min-width: min-content;
}

.example-icon {
    padding: 0 14px;
  }

  .example-spacer {
    flex: 1 1 auto;
  }

  .mat-sidenav-content{
      font-size: 200%;
      text-align: center;
  }
Tomasz Chudzik
  • 1,867
  • 1
  • 14
  • 21
  • Great one. I just wanted to let you know that, after opening the menu in small screen, how about closing it automatically as the screen size increases. – Raju Aug 02 '18 at 04:54
  • Thanks for this advice. It was enough to add fxHide.gt-sm="true" to mat-sidenav. The sidenav disappears. All that remains is the darkened main content, but it vanishes after a click anywhere. – Tomasz Chudzik Aug 02 '18 at 10:40
  • This is awesome. Thanks @TomaszChudzik . For future readers, in case you get error(in my case it was `MediaQueryList`) in compiling flexbox remember to move to lower versions. Just run `npm install @angular/flex-layout@6.0.0-beta.18 ` – Pramesh Bajracharya Oct 18 '18 at 10:48
  • 1
    I'm using this great example but on a large screen I get a vertical scrollbar because `height: 100%; min-height: 100%;` for `mat-sidenav-container` is set. This makes the container larger than the page, with the height of the top menu. I've been trying to solve this but can't get it to work. – Paul Meems Oct 19 '18 at 10:18
  • @PaulMeems you were right and it could be annoying. I changed the Toolbar height to `7vh` and `mat-sidenav-container` height to `min-height: 93vh !important;` The `!imporant` allows us to override the element.style. It's not always the best way but in this case it's the only solution I could find – Tomasz Chudzik Oct 19 '18 at 12:38
  • 1
    I found a different, but simar solution. change the first `
    ` to `
    `, where `65px` is the size of your toolbar.
    – Paul Meems Oct 19 '18 at 12:53
  • Nice article. Fixed my code – Ansif Jul 20 '22 at 06:07
17

Problem

You need a way to make your navigation bar responsive.

Solution

You can use Material Angular with ToolBar and Flex-Layout.

Example

npm install @angular/flex-layout --save

Example Angular Material Toolbar with Flex Layout.

<div style="height: 100vh;">

  <mat-toolbar color="primary">

    <span>Responsive Navigation</span>

    <span class="example-spacer"></span>

    <div fxShow="true" fxHide.lt-md="true">

      <!-- The following menu items will be hidden on both SM and XS screen sizes -->

      <a href="#" mat-button>Menu Item 1</a>

      <a href="#" mat-button>Menu Item 2</a>

      <a href="#" mat-button>Menu Item 3</a>

      <a href="#" mat-button>Menu Item 4</a>

      <a href="#" mat-button>Menu Item 5</a>

      <a href="#" mat-button>Menu Item 6</a>

    </div>

    <div fxShow="true" fxHide.gt-sm="true">

      <a href="#" (click)="sidenav.toggle()">Show Side Menu</a>

    </div>

  </mat-toolbar>

  <mat-sidenav-container fxFlexFill class="example-container">

    <mat-sidenav #sidenav fxLayout="column">

      <div fxLayout="column">

        <a (click)="sidenav.toggle()" href="#" mat-button>Close</a>

        <a href="#" mat-button>Menu Item 1</a>

        <a href="#" mat-button>Menu Item 2</a>

        <a href="#" mat-button>Menu Item 3</a>

        <a href="#" mat-button>Menu Item 4</a>

        <a href="#" mat-button>Menu Item 5</a>

        <a href="#" mat-button>Menu Item 6</a>

      </div>

    </mat-sidenav>

    <mat-sidenav-content fxFlexFill>Main content</mat-sidenav-content>

  </mat-sidenav-container>

</div>
wuno
  • 9,547
  • 19
  • 96
  • 180
  • 1
    I had already used bootstrap components to make the navbar but i want to make use of angular material components not bootstrap and make them responsive – INFOSYS Apr 04 '17 at 07:47
  • 2
    Yes but i need the same feel of the material components , the way the material ui look i want the nav bar to be like that even the links – INFOSYS Apr 04 '17 at 08:26
  • 1
    This is for angular js , is ot applicable to angular 2 also. As the new material is build upside down – INFOSYS Apr 04 '17 at 13:26
  • @wuno - I think there needs to make some changes. Where to put the main content (I mean the body of each menu/link). Now it is inside . I don't think that is the right way. – SK. Nov 19 '18 at 22:22
  • That is a pretty horrible tutorial, every piece of code in the introduction is different from his final code.. and it doesn't work as he pictured. – Brunis Apr 17 '21 at 08:09
  • 1
    Thank you for letting me know. I updated the answer to remove the link to the outdated tutorial. – wuno Apr 18 '21 at 13:56
5

Use angular/flex-layout for responsiveness.

Edric
  • 24,639
  • 13
  • 81
  • 91
1

To see how the official Angular website does it, see these components (on Angular 15, replace commit hash 6706fab with main for latest):

import {Component, Input, OnChanges} from '@angular/core';
import {NavigationNode} from 'app/navigation/navigation.model';

@Component({
  selector: 'aio-nav-item',
  templateUrl: 'nav-item.component.html',
})
export class NavItemComponent {
  @Input() isWide = false;

Later:

<aio-nav-item *ngFor="let node of nodeChildren" [level]="level + 1" [isWide]="isWide"
    [isParentExpanded]="isExpanded"
    [node]="node" [selectedNodes]="selectedNodes"></aio-nav-item>

And:

  <nav [attr.aria-label]="navLabel || null">
    <aio-nav-item *ngFor="let node of filteredNodes"
      [node]="node"
      [selectedNodes]="selectedNodes"
      [isWide]="isWide">
    </aio-nav-item>
  </nav>`

With a whole bunch of SCSS to handle it https://github.com/angular/angular/blob/6706fab/aio/src/styles/1-layouts/top-menu/_top-menu.scss

@use '../../mixins';
@use '../../constants';

// VARIABLES
$showTopMenuWidth: 1150px;
$hideTopMenuWidth: $showTopMenuWidth - 1;
$hamburgerShownMargin: 0 8px 0 0;
$hamburgerHiddenMargin: 0 16px 0 -64px;

// DOCS PAGE / STANDARD: TOPNAV TOOLBAR FIXED
mat-toolbar.app-toolbar {
  position: fixed;
  top: 0;
  right: 0;
  left: 0;
  z-index: 10;
  box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.3);

  // HOME PAGE OVERRIDE: TOPNAV TOOLBAR
  .page-home & {
    @media (min-width: 481px) {
      &:not(.transitioning) {
        background-color: transparent;
        box-shadow: none;
        position: absolute;
        transition: background-color 0.2s linear;
      }
    }
  }

  // DOCS PAGES OVERRIDE: HAMBURGER
  @include mixins.docs-pages($nestParentSelector: true) {
    @media (min-width: $showTopMenuWidth) {
      .hamburger {
        // Hamburger shown on non-marketing pages even on large screens.
        margin: $hamburgerShownMargin;
        visibility: visible;
      }
    }
  }

  mat-toolbar-row {
    padding: 0 16px 0 0;
  }

  // HAMBURGER BUTTON
  .hamburger {
    height: 100%;
    margin: $hamburgerShownMargin;
    padding: 0;

    @media (min-width: $showTopMenuWidth) {
      // Hamburger hidden by default on large screens.
      // (Will be shown per doc.)
      margin: $hamburgerHiddenMargin;
      visibility: hidden;
    }

    @media (max-width: 480px) {
      min-width: 15%;
    }

    &:not(.no-animations) {
      transition-duration: 0.4s;
      transition-property: color, margin;
      transition-timing-function: cubic-bezier(0.25, 0.8, 0.25, 1);
    }

    & .mat-icon {
      position: inherit;
    }
  }
}
A T
  • 13,008
  • 21
  • 97
  • 158
0

My crude implementation attempt with flex-layout and angular-material 5.0.2

.mat-sidenav-container {
  background: rgba(0, 0, 0, 0.08);
}

.blank-grow {
  flex: 1 1 auto;
}
<mat-sidenav-container fullscreen>
  <mat-sidenav #sidenav>
    <mat-nav-list>
      <a mat-list-item>
        <mat-icon mat-list-icon>home</mat-icon>
        <span mat-line>home</span>
      </a>

      <a mat-list-item>
        <mat-icon mat-list-icon>backup</mat-icon>
        <span mat-line>Backup</span>
      </a>
    </mat-nav-list>
  </mat-sidenav>
  <mat-toolbar color="primary">
    <button mat-icon-button (click)="sidenav.open()" fxHide="false" fxHide.gt-sm>
      <mat-icon>menu</mat-icon>
    </button>
    <span> Big Header</span>
    <span class="blank-grow"></span>
    <div fxLayout="row" fxShow="false" fxShow.gt-sm>
      <a>
        <mat-icon mat-list-icon>home</mat-icon>
        <span mat-line>home</span>
      </a>

      <a>
        <mat-icon mat-list-icon>backup</mat-icon>
        <span mat-line>Backup</span>
      </a>
    </div>
  </mat-toolbar>
</mat-sidenav-container>
Edric
  • 24,639
  • 13
  • 81
  • 91
Anup
  • 600
  • 3
  • 9