Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I close Material SideNav by clicking on an element which has the RouterLink directive?

I am using angular 4 in combination with angular material.

I am using a sidenav container a router and a custom generated menu:

<mat-sidenav-container>
<mat-sidenav #sidenav>
    <app-menu></app-menu>
</mat-sidenav>
<router-outlet></router-outlet>
</mat-sidenav-container>

Inside the menu component I am using default angular 4 router links:

<ul>
  <li *ngFor="let hero of heroes">
    <a mat-button [routerLink]="['/hero/', hero.id]" queryParamsHandling="merge">
      {{hero.name}}
    </a>
  </li>
</ul>

What I would like to do is to close the Material sidenav when clicking on one of those router links.

I can assure you that without the click event routing works fine, it just does not close my sidenav, because the router-outlet is inside the side navigation.

However, when I add a (click)="sidenav.close()" to the a my full page suddenly refreshed, instead of just following the router link.

I have tried many things, but I can't seem to figure it out, hope someone can help!

like image 841
Nick N. Avatar asked Oct 25 '17 14:10

Nick N.


2 Answers

Here's a complete solution based off of Angular Material's schematics generated sidenav layout component. This answer improves on the previous ones by taking into consideration whether we are on a handset device or not, and making sure the sidenav only closes on a handset device.

A few points:

  1. This component was generated by using Angular Materials navigation schematics.
  2. I called my component my-menu however, you may use any other name.
  3. The sidenav will only close if we are not on a handset device, this is done using the rxjs operator withLatestFrom combined with the isHandset$ stream which was generated by the schematics.
import { Component, ViewChild } from '@angular/core';
import { BreakpointObserver, Breakpoints, BreakpointState } from '@angular/cdk/layout';
import { Observable } from 'rxjs';
import { map, filter, withLatestFrom } from 'rxjs/operators';
import { Router, NavigationEnd } from '@angular/router';
import { MatSidenav } from '@angular/material';

@Component({
  selector: 'app-my-menu',
  templateUrl: './my-menu.component.html',
  styleUrls: ['./my-menu.component.css']
})
export class MyMenuComponent {
  @ViewChild('drawer') drawer: MatSidenav;

  isHandset$: Observable<boolean> = this.breakpointObserver.observe(Breakpoints.Handset)
    .pipe(
      map(result => result.matches)
    );

  constructor(private breakpointObserver: BreakpointObserver,
    router: Router) {
    router.events.pipe(
      withLatestFrom(this.isHandset$),
      filter(([a, b]) => b && a instanceof NavigationEnd)
    ).subscribe(_ => this.drawer.close());
  }
}
like image 125
Aviad P. Avatar answered Oct 05 '22 22:10

Aviad P.


I created a service so that the sidenav could be controlled from various components. I followed this example.

Then it's a matter of watching the router for events and closing as needed. In app.component.ts:

this.router.events.subscribe(event => {
  // close sidenav on routing
  this.sidenavService.close();
});

This doesn't work if you want to close the sidenav on only specific links, but the service itself would facilitate that.

like image 39
isherwood Avatar answered Oct 05 '22 22:10

isherwood