Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

md-sidenav: make it automatically responsive #1130

Open
alexjoverm opened this issue Aug 29, 2016 · 12 comments
Open

md-sidenav: make it automatically responsive #1130

alexjoverm opened this issue Aug 29, 2016 · 12 comments
Labels
area: material/sidenav feature This issue represents a new feature or feature request rather than a bug or bug fix P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent

Comments

@alexjoverm
Copy link

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.

@jelbourn jelbourn added feature This issue represents a new feature or feature request rather than a bug or bug fix P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent labels Aug 31, 2016
@jelbourn
Copy link
Member

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

@GaryChamberlain
Copy link

GaryChamberlain commented Oct 18, 2016

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

@sikemullivan
Copy link

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();
}
});
}`

@alexjoverm
Copy link
Author

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

@pauldemarco
Copy link

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

@krisklosterman
Copy link

krisklosterman commented Mar 23, 2017

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>

@ajaysattikar
Copy link

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

@Gusachenko
Copy link
Contributor

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();
    }
  }
  
}

@angularexample
Copy link

angularexample commented May 8, 2017

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.

@Denis-Frolov
Copy link

Denis-Frolov commented May 19, 2017

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:

  • this.media
  • change:MediaChange

@jawadst
Copy link

jawadst commented May 27, 2017

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

@Denis-Frolov
Copy link

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: material/sidenav feature This issue represents a new feature or feature request rather than a bug or bug fix P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent
Projects
None yet
Development

Successfully merging a pull request may close this issue.