Search code examples
angularangular2-routingangular2-templateangular4-router

Toggle multiple side navs in angular 4


My component structure is

app
  --app.component.html
  --app.component.ts
  -dashboard
    --dashboard.component.html
    --dashboard.component.ts
  -sales
    --sales.component.html
    --sales.component.ts
  -taxreports
    --taxreports.component.html
    --taxreports.component.ts
  -services
    --commonservices.ts

My screen design

enter image description here

Here is my code for app.component.html

<div class="BodyStyle">
    <div class="container FirstRowContainer" fxLayout="row" fxLayoutAlign="center" fxLayoutGap="0px">
        <div class="container FirstColumnContainer" fxLayout="column" fxLayoutAlign="center" fxLayoutGap="0px">
            <div class="item" fxFlex="16%">
                <img class="LogoStyle" src="/src/app/Shared/Images/test.png" height="55" />
            </div>
        </div>

        <div class="container SecondColumnContainer" fxLayout="column" fxLayoutAlign="center" fxLayoutGap="0px">
            <div class="container SecondRowContainer" fxLayout="row" fxLayoutAlign="center" fxLayoutGap="0px">
                <div class="container" fxLayout="column" fxLayoutAlign="center" fxLayoutGap="0px">
                    <div class="LogoHeading" fxFlex="100%">
                        <h2 class="restaurantname">Name</h2>
                    </div>
                </div>
                <div class="container" fxLayout="column" fxLayoutAlign="center" fxLayoutGap="0px">
                    <mat-toolbar class="UserToolBarStyle">
                        <i class="material-icons UserIconStyle">
                            person
                            <button mat-button class="UserButtonStyle">
                                <h4 class="UserNameStyle">Admin</h4>
                            </button>
                        </i>
                    </mat-toolbar>
                </div>
            </div>

            <div class="container ThirdRowContainer" fxLayout="row" fxLayoutAlign="center" fxLayoutGap="0px">
                <div class="container ThirdColumnContainer" fxLayout="column" fxLayoutAlign="center" fxLayoutGap="0px">

                    <div class="item" fxFlex="100%">
                        <mat-toolbar class="ToolBarStyle">
                            <button mat-button class="ButtonMenu" (click)="btnClick('dashboard');">Dashboard</button>
                            <button mat-button class="ButtonMenu" (click)="toggleSidenav('sales')">Sales</button>
                            <button mat-button class="ButtonMenu" (click)="toggleSidenav('taxreports')">Tax Reports</button>
                            <button mat-button class="ButtonMenu">MIS</button>
                            <button mat-button class="ButtonMenu">Others</button>
                        </mat-toolbar>
                    </div>
                </div>

                <div class="container FourthColumnContainer" fxLayout="column" fxLayoutAlign="center" fxLayoutGap="0px">
                    <div class="item">
                        <mat-toolbar class="ToolBarStyle">
                            <i class="glyphicon glyphicon-log-out UserIconStyle1">
                                <button mat-button class="UserButtonStyle1">
                                    <h4 class="UserNameStyle1">Logout</h4>
                                </button>
                            </i>
                        </mat-toolbar>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <div class="placeholder" fxLayout="row" fxLayoutAlign="center" fxLayoutGap="0px" style="margin-top:50px;margin-left:20px;margin-right:20px;">
         <div [hidden]="salesDiv"class="item" fxFlex="100%">               
            <app-tax-reports>
                <router-outlet></router-outlet>
            </app-tax-reports>
             <app-sales>
                 <router-outlet></router-outlet>
             </app-sales> 
        </div>
        <div [hidden]="salesDiv" class="item" fxFlex="100%">
            <app-sales>
                <router-outlet></router-outlet>
            </app-sales>                           
        </div>

    </div>

    <div class="container FooterContainer" fxLayout="column" fxLayoutAlign="center" fxLayoutGap="0px">
        <div class="item item-4 FooterItem" fxFlex="100%" fxFlexOrder="2">
            <footer>
                <p class="footer_align">
                    <span class="FooterText">Powered By</span>
                    <img class="FooterImage" src="/src/app/Shared/Images/EasyResto.png" />
                </p>
            </footer>
        </div>
    </div>
</div>

Here is my app.component.ts

import { Component, Input } from '@angular/core';
import { HttpservService } from './services/commonservice.service';
import { MatSidenav } from '@angular/material';

@Component({
    selector: 'my-app',
    templateUrl: './app.component.html',    
    providers: [HttpservService]
})
export class AppComponent {
    salesDiv: boolean;
    taxDiv: boolean;


    constructor(private sidenavService: HttpservService) {
        this.salesDiv = true;
        this.taxDiv = true;
    }

    toggleSidenav(div: string) {

        if (div == 'sales') {
            alert("sales");
            this.salesDiv = false;
            this.taxDiv = true;
            this.sidenavService.toggle();
        }
        else if (div == 'taxreports') {
            alert("taxreports");
            this.salesDiv = true;
            this.taxDiv = false;
            this.sidenavService.toggle();
        }

    }
}

Here is my sales component.html

<mat-sidenav-container>
    <mat-sidenav #sidenav mode="side" class="mat-elevation-z4">
        Sidenav
    </matmd-sidenav>
  <main>
      <ng-content></ng-content>
  </main>
</mat-sidenav-container>

Here is my sales.component.ts

import { Component, OnInit, ViewChild } from '@angular/core';
import { HttpservService } from '../services/commonservice.service';
import { MatSidenav } from '@angular/material';
import { Observable } from 'rxjs/Observable';
import { Router } from '@angular/router';

import 'rxjs/add/operator/do';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';

@Component({
    selector: 'app-sales',
    templateUrl: './sales.component.html'
})
export class SalesComponent implements OnInit {   


    @ViewChild('sidenav') public sidenav: MatSidenav;

    constructor(private httpservice: HttpservService, private router: Router) {       
    }

    ngOnInit() {
        console.log(this.sidenav);
        this.httpservice.setSidenav(this.sidenav);
    }

}

In commonservice.ts I have called the toggle and sidesetnav like this

 public setSidenav(sidenav: MatSidenav) {
    this.sidenav = sidenav;
}

 public toggle(isOpen?: boolean): Promise<MatDrawerToggleResult> {

    return this.sidenav.toggle(isOpen);
}

When I click the sales menu, the side nav toggles perfectly. But when I click tax reports menu the side nav doesn't toggle. It just shows a blank page in the content area. I couldn't find what side bar is passed to the service during run time. I am calling the setSidenav() in both sales and tax reports oninit(). So what happens is that, the last side bar that is passed is set to the sidenav and only that toggles. I don't understand what mistake I am doing. I even tried to create 2 separate function as setSideNav1() and toggle1() but still the same issue.

Edit: @Llorenc Pujol Ferriol : I made few changes in your code. This is what I have done and it works perfectly.

   <div class="placeholder" fxLayout="row" fxLayoutAlign="center" fxLayoutGap="0px" style="margin-top:50px;margin-left:20px;margin-right:20px;">
    <div fxFlex="100%">
        <router-outlet></router-outlet>
    </div>
    <mat-sidenav-container>
        <mat-sidenav #sidenav align="end" mode="side" class="mat-elevation-z4 side-nav-overflow-hidden">
            <app-sales *ngIf="salesDiv">
            </app-sales>
        </mat-sidenav>
    </mat-sidenav-container>

    <mat-sidenav-container>
        <mat-sidenav #taxnav mode="side" align="end" class="mat-elevation-z4">
            <app-tax-reports *ngIf="taxDiv">
            </app-tax-reports>
        </mat-sidenav>
    </mat-sidenav-container>
</div>

But the issue is the container takes the major portion of width and i couldnt set width for matside-nav-container. When I set it acts weird. Here is the screen shot of it.

Dashboard and side bar

Update: I have fixed the width problem by adding [ngStyle]="{ 'width.vw': setSalesWidth }"


Solution

  • I see two problems here. The first problem is that the condition to show your components is not different each other, both are hidden when salesDiv is true:

        <div [hidden]="salesDiv"class="item" fxFlex="100%">               
            <app-tax-reports>
                <router-outlet></router-outlet>
            </app-tax-reports>
             <app-sales>
                 <router-outlet></router-outlet>
             </app-sales> 
        </div>
        <div [hidden]="salesDiv" class="item" fxFlex="100%">
            <app-sales>
                <router-outlet></router-outlet>
            </app-sales>                           
        </div>
    

    The second and MOST IMPORTANT, is that the component architecture you have done here is not the expected. The Material2 sidenav component is not desired to be destroyed and initilized over again, It has relative animations and current state, is not a good practise, although, there is no reason to destroy it, just replace its content:

    <mat-sidenav-container>
        <mat-sidenav #sidenav mode="side" class="mat-elevation-z4">
           <app-sales-sidenav *ngIf="salesDiv"></app-sales-sidenav>
           <app-taxes-sidenav *ngIf="taxDiv"></app-taxes-sidenav>
        </mat-sidenav>
        <app-sales *ngIf="salesDiv"></app-sales>
        <app-taxes *ngIf="taxDiv"></app-taxes>
    </mat-sidenav-container>
    

    This implementation is done with *ngIf, it also could be done a dynamic component creation. Something like that: Creating a angular2 component with ng-content dynamically

    This would make the comunication between components also harder, because it is separeted, but the final result is much better, it also hels decouple your application.

    Hope this helps.