'Angular 2 change sidenav content depending on the current router state

I am developing single page application in Angular 2. I am using angular material mat-sidenav and mat-toolbar. Toolbar has sidenav toggle button, buttons to open 'Clients' section and 'Reports' section. This structure is common throughout the project.

So the app looks like this:
project template

I have to change the content in the sidenav (and in the main field) depending on the current state of the router. So, if the 'Clients' section is opened the sidenav should show some subsections related to the clients. Similarly, for 'Reports' section only subsections related to the reports should be available. How do I do it?



Solution 1:[1]

I have figured out how to do this with the layout approach. You can see the example of implementation of the admin system using this approach here

Solution 2:[2]

I've done that by checking if URL path contains some specified part. For example if you want to have visible submenu for clients it could be '/clients' etc. Then you just use *ngIf directive:

<ul *ngIf="location.includes('/clients')">

To check current URL you have to listen for router changes. So, in the component where you have sidenav content you need to subscribe to router.events property.

import { Router, NavigationEnd } from '@angular/router';
import 'rxjs/add/operator/filter';
...
export class SidenavComponent {
  location = '';
  constructor(router: Router) {
    router.events
      .filter(event => event instanceof NavigationEnd)
      .subscribe((event: NavigationEnd) => {
        this.location = event.url
      })
...

Apart of NavigationEnd router emit multiple other events so we filtering them in example above and after subscription, we're updating location property

Solution 3:[3]

Another alternative is to use the [hidden] property. I started out using *ngIf but that recreates the component, which in my case retriggered some filtering that caused the main content to redraw. [hidden] allows me to selectively display content without component destruction. Usage like this:

<mat-sidenav-container class="sidenav-container">
    <mat-sidenav>
        <div >
            <div [hidden]="activeContent !== 'sidenav1'">
                <p>SIDENAV 1</p>
            </div>
            <div [hidden]="activeContent !== 'sidenav2'">
                <p>SIDENAV 2</p>
            </div>
        </div>
    </mat-sidenav>

    <mat-sidenav-content>
        <p>MAIN CONTENT<p>
    </mat-sidenav-content>
</mat-sidenav-container>

Notes:

  • activeContent is a public field on the component class containing the sidenav name (or more specifically, a string literal union type for design-time type checking)
  • If you're using side mode for the sidenav and you have inconsistent widths across your sidenav content, you may observe that the main content does not resize to fill the available space. The best solution I came up with for this is to trigger this._sidenav.close().then(_ => this._sidenav.open()); in the component class when the sidenav content changes.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Nikita Marinosyan
Solution 2 Lothir
Solution 3 tek