Components: md-sidenav: make it automatically responsive

Created on 29 Aug 2016  路  12Comments  路  Source: angular/components

_Feature request_
Version: Angular Material 2.0.0-alpha.7 wax-umpire

I'd like to have an option where the sidenav can be responsive automatically, by having a breakpoint where the sidenav is shown by default when the width is higher than X.

This was already implemented in Angular Material (1) as the md-is-locked-open: https://material.angularjs.org/latest/api/directive/mdSidenav

Is there any plan to have this? I could submit a PR if this is wanted.

P3 materiasidenav feature

Most helpful comment

Here are 2 ways to make mdsidenav responsive automatically.

  1. You can bind to the window resize event and open or close the sidenav by doing everything in the template:
<md-sidenav #xxxMenu (window:resize)="$event.target.innerWidth<769?xxxMenu.close():xxxMenu.open()"> 
  1. Or, here is how to make MdSidenav automatically responsive, using the new FlexLayoutModule

In your component,

  1. Set a public boolean "isMobileView" by examining the media observable "isActive".
  2. Subscribe to the "MediaChange" to responsively change the boolean that will control the state of the sidenav.
  ngOnInit():void {
    this.isMobileView = (this.media.isActive('xs') || this.media.isActive('sm'));

    this.subscriptionMedia = this.media.subscribe((change:MediaChange) => {
      this.isMobileView = (change.mqAlias === 'xs' || change.mqAlias === 'sm');
    });

In your component, add a responsive handler for link click:

  onLinkClick():void {
    if (this.isMobileView) {
      this.menuSidenav.close();
    }
  }

The onLinkClick() handler needs to added to each of the menu links in your template.
It will close the menu after clicking a link, but not when the menu should stay open.

In your template, bind the md-sidenav's "mode", "opened", and "disableClose" attributes to your component's responsive boolean.

<md-sidenav #xxxMenu mode="{{isMobileView?'over':'side'}}" opened="{{!isMobileView}}" disableClose="{{!isMobileView}}">

Now, based on your responsive breakpoint...

  • The sidenav will be in the correct state at the initial view.
  • The sidenav mode will change from "side" to "over".
  • The sidenav will automatically open or close.
  • The disableClose prevents closing with the escape key.

All 12 comments

We definitely plan to do this- so far we haven't yet dove into all of the responsive behaviors for various components.

It would be great if it worked like the left sidebar in the Firebase console.

Is this doable for now? I really like the menu being locked open like Alex stated. Just added this to app.component.ts till this is supported. I'm new to Angular in general.

` @ViewChild("start") private start: MdSidenav;
constructor(private _ngZone: NgZone) { }

ngOnInit() {
//Total hack
window.onresize = (e) => {
this.checkMenu();
};
this.checkMenu();
}

checkMenu() {
this._ngZone.run(() => {
var w = window.innerWidth;
if (w > 768) {
this.start.open();
} else {
this.start.close();
}
});
}`

I did it in the FRP + Redux way (but of course you can ommit the Redux stuff). In case you wanna check it out: https://github.com/alexjoverm/Footle/blob/master/client/src/app/containers/sidenav/sidenav.component.ts

sikemullivan, that totally works, have you noticed any compatibility issues?

This seems to work too:

import { Component, ViewChild, OnInit, HostListener } from '@angular/core';
import { MdSidenav } from "@angular/material";

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  @ViewChild('sidenav') sidenav: MdSidenav;

  constructor(
  ) { }

  ngOnInit() {
  }

  @HostListener('window:resize', ['$event'])
    onResize(event) {
        if (event.target.innerWidth < 500) {
            this.sidenav.close();
        }
        if (event.target.innerWidth > 500) {
           this.sidenav.open();
        }
    }
}

Another idea

import { Component, ViewChild, OnInit, HostListener } from '@angular/core';
import { MdSidenav } from "@angular/material";

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {

  @ViewChild('sidenav') sidenav: MdSidenav;
  navMode = 'side';

  constructor(
  ) { }

  ngOnInit() {
    if (window.innerWidth < 768) {
      this.navMode = 'over';
    }
  }

  @HostListener('window:resize', ['$event'])
    onResize(event) {
        if (event.target.innerWidth < 768) {
            this.navMode = 'over';
            this.sidenav.close();
        }
        if (event.target.innerWidth > 768) {
           this.navMode = 'side';
           this.sidenav.open();
        }
    }
}

HTML

<md-sidenav-container class="wrapper">
    <md-sidenav #sidenav mode="{{navMode}}" opened="true" class="app-sidenav">
    <app-sidebar></app-sidebar></div>
    </md-sidenav>
    <div class='main-container'>
        <app-navbar></app-navbar>
        <router-outlet></router-outlet>
    </div>
</md-sidenav-container>

That's perfect, thanks!!
However I was wondering if there any solution for "sidenav collapse to show icons".

Here's another way, with latest Renderer2 module:

import { Component } from '@angular/core';
import { ViewChild,  Renderer2  } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})

export class AppComponent {
  @ViewChild('sidenav') m_sidenav;

  constructor(private renderer: Renderer2){
    this.checkWindowWidth();
    renderer.listen(window, 'resize', (event) => {
      this.checkWindowWidth();
    });   
  }

  private checkWindowWidth() : void {
    if(window.innerWidth > 768){
      this.m_sidenav.close();
    }else{
      this.m_sidenav.open();
    }
  }

}

Here are 2 ways to make mdsidenav responsive automatically.

  1. You can bind to the window resize event and open or close the sidenav by doing everything in the template:
<md-sidenav #xxxMenu (window:resize)="$event.target.innerWidth<769?xxxMenu.close():xxxMenu.open()"> 
  1. Or, here is how to make MdSidenav automatically responsive, using the new FlexLayoutModule

In your component,

  1. Set a public boolean "isMobileView" by examining the media observable "isActive".
  2. Subscribe to the "MediaChange" to responsively change the boolean that will control the state of the sidenav.
  ngOnInit():void {
    this.isMobileView = (this.media.isActive('xs') || this.media.isActive('sm'));

    this.subscriptionMedia = this.media.subscribe((change:MediaChange) => {
      this.isMobileView = (change.mqAlias === 'xs' || change.mqAlias === 'sm');
    });

In your component, add a responsive handler for link click:

  onLinkClick():void {
    if (this.isMobileView) {
      this.menuSidenav.close();
    }
  }

The onLinkClick() handler needs to added to each of the menu links in your template.
It will close the menu after clicking a link, but not when the menu should stay open.

In your template, bind the md-sidenav's "mode", "opened", and "disableClose" attributes to your component's responsive boolean.

<md-sidenav #xxxMenu mode="{{isMobileView?'over':'side'}}" opened="{{!isMobileView}}" disableClose="{{!isMobileView}}">

Now, based on your responsive breakpoint...

  • The sidenav will be in the correct state at the initial view.
  • The sidenav mode will change from "side" to "over".
  • The sidenav will automatically open or close.
  • The disableClose prevents closing with the escape key.

Very nice answer, (the one about FlexLayoutModule). But FlexLayoutModule docs are as clear as mud. So could you please be a bit more specific here:
what types are:

@Denis-Frolov
They have a complete API doc with an example and the right types here: https://github.com/angular/flex-layout/wiki/ObservableMedia

@jawadst
Oh thanks, I did not see that one.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

vanor89 picture vanor89  路  3Comments

kara picture kara  路  3Comments

theunreal picture theunreal  路  3Comments

MurhafSousli picture MurhafSousli  路  3Comments

LoganDupont picture LoganDupont  路  3Comments