elquesogrand
elquesogrand

Reputation: 197

How would I toggle my sidebar in another component?

I have a header component with a mat-toolbar which I have a button that I want to use to toggle my sidenav bar which is in another component. How would I toggle my sidebar in another component?

I want to toggle it below the header component.

https://stackblitz.com/edit/angular-adkpkv

I copied my whole project in here but I can't seem to get it to run in stackbitz. It does run locally though.

Upvotes: 3

Views: 11311

Answers (3)

L. Guthardt
L. Guthardt

Reputation: 2056

I suggest you using services. The advantages are that they are very easy to get started with and easy to use. Furthermore a service is accessible of every component or even other services, you can toggle the sidebar from as many different components as you want. If you are familiar with e.g. C#, a service behaves similar to a singleton in C#.

So you should create a new service, by typing ng g s toggle into the Angular CLI.

import { Injectable } from '@angular/core';

@Injectable({
    providedIn: 'root'
})
export class ToggleService {

    constructor() { }
}

Solution 1:

This is the simpler way without a nice animation. The easiest way to toggle your sidebar component is by placing it already at the correct position, but setting display: none. Then you use ngClass to set a css class to the sidebar component, depending on a property. Apply this code into the tag of the top most one of your sidebar component.

[ngClass]="{'show-sidebar': toggleService.showSidebar}"

In order for that to work, you have to create a new css class in the sidebar's css. Add:

.show-sidebar { 
    display: block;
}

After that you add a property to the service.

private _showSidebar = false;
public get showSidebar(): boolean{
    return this._showSidebar;
}
public set showSidebar(v : boolean) {
    this._showSidebar = v;
}

Now you inject the service into the sidebar component and assign it to a property to be able to reference the service in your html file. So you can use the bool value of showSidebar to toggle the show-sidebar css class.

The next thing is just to inject the service into the component where the button is, that you use to toggle the sidebar. After that you add a click event to the button where you change the value of toggleService.showSidebar to true.


Solution 2:

Now if you want to use e.g. Angular Animations you can add a property to the service that you can change from one compontent and that change will toggle the animation state to animate your sideBar to move into the screen.

private _sideBarState = 'inactive';
public get sideBarState(): string {
    return this._sideBarState;
}
public set sideBarState(v : string) {
    this._sideBarState = v;
}

Next you bind a sliderState to your component in html, so in the html of the sidebar component. Just add this into the tag of the top most one in your sidebar.

[@sliderState]="toggleService.sideBarState"

To be able to do that you have to inject the toggleService into your sidebar component before. If you have question about that, just go ahead and ask me.

After that you have to add the different animations in the decorator of the component that you want to animate, the sidebar component.

@Component({
selector: 'core-detail-page',
templateUrl: './detail-page.component.html',
styleUrls: ['./detail-page.component.scss'],
animations: [
    trigger('sliderState', [
        state('active', style({
            left: '0',
            display: 'block'
        })),
        state('inactive', style({
            left: '-300px',
            display: 'none'
        })),
        transition('inactive => active', animate('300ms ease-out')),
        transition('active => inactive', animate('300ms ease-out'))
    ])
]

})

Those are just some dummy values where I pretend that your sidebar component is set to position: absoulte and placed right next to your screen, those -300px. And once you change the value of the sidebarState in the toggleService to active the property left and display will be animated due to your defintions of the animation. You can animate any property you want.

Changing the sidebarState is done by injecting the toggleService into the component where your button, which you want to use to toggle the sidebar, is located. After injecting the service, you add a click method to the button and in this method you change the value of sidebarState.

toggleSidebar() {
    this.toggleService.sideBarState = 'active';
}

Upvotes: 2

Qellson
Qellson

Reputation: 552

I had the same problem few weeks back and I came across this article that helped me alot. Its explaining how components communicate by toggling a sidebar :)

https://medium.com/@mirokoczka/3-ways-to-communicate-between-angular-components-a1e3f3304ecb

Cheers!

Upvotes: 0

Akber Iqbal
Akber Iqbal

Reputation: 15041

You gotta make a few changes (below) to your existing stackblitz which will

  • take each click (of burger menu) out from header.component
  • receive this value inside app.component (parent) and pass to child
  • handle this click in side-nav.component (child)

Make these changes & see the toggle reaching from one component to the other... I have added a <mark> tag inside the child to check that the value is indeed received there.

change your header.component.ts to be like:

import { Component, OnInit, Output, EventEmitter } from '@angular/core';

@Component({
  selector: 'app-header',
  templateUrl: './header.component.html',
  styleUrls: ['./header.component.css']
})
export class HeaderComponent implements OnInit {
  @Output() menuState = new EventEmitter();

  constructor() { }
  opened: boolean;
  showMenu = false; /* false by default, since hidden */
  toggleMenu() {
      console.log("inside toggleMenu");
      this.showMenu = !this.showMenu;
      this.menuState.emit(this.showMenu);
   }
  ngOnInit() {
  }

}

your header.component.html to be:

<mat-toolbar>
    <mat-toolbar-row>
        <a href="" class="site-logo">
            <h2>TEST APP</h2>
        </a>
        <button mat-icon-button (click)="toggleMenu()"> 
              <mat-icon>menu </mat-icon>
          </button>
      <span class="example-spacer"></span>
      <mat-icon class="example-icon"  matBadge="15" matTooltip="Recent changes">notifications</mat-icon>
      <mat-icon class="example-icon"  matTooltip="Info or how to contact us">help</mat-icon>
      <mat-icon class="example-icon"  matTooltip="Your account information"> account_circle</mat-icon>
    </mat-toolbar-row>

</mat-toolbar>

your app.component.ts to be:

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'This is the copy of the Expense App';
  subMenuState:boolean = false;
  burgerClicked(evnt){
    this.subMenuState = evnt;
    console.log("inside burgerClicked: pls. change showMenu to be:",this.subMenuState);
  }

}

your app.component.html to be:

<h1>{{title}}</h1>
<app-header (menuState)='burgerClicked($event)'></app-header>
<app-side-nav [subMenuState]="subMenuState"></app-side-nav>
<router-outlet></router-outlet>
<app-footer></app-footer>

your side-nav.component.ts to be

import { Component, OnInit, Input, OnChanges } from '@angular/core';

@Component({
  selector: 'app-side-nav',
  templateUrl: './side-nav.component.html',
  styleUrls: ['./side-nav.component.css']
})
export class SideNavComponent implements OnInit, OnChanges {
  @Input() subMenuState;
  constructor() { }
  opened: boolean;
  showMenu = true;
  toggleMenu() {
      this.showMenu = !this.showMenu;
   }
  ngOnInit() {
  }

  ngOnChanges(){
    console.log("inside ngOnChanges with subMenuState: ",this.subMenuState );
    this.showMenu = this.subMenuState;
  }

}

in your side-nav.component.html, I added a row at the top to ensure that value is toggling correctly

<mark> Show menu (inside side-nav.component) ? {{showMenu}} </mark>
<mat-sidenav-container >
  <mat-sidenav #sidenav mode="side"  mat-disable-backdrop #start >
      <mat-nav-list>
          <a mat-list-item routerLink="/">  
              <mat-icon> home</mat-icon>
              <span> All Reports </span>
          </a>
          <a mat-list-item routerLink="/entries">  
              <mat-icon>  assignment </mat-icon>
              <span> Reports Per Business </span>
          </a>
          <a mat-list-item (click)="toggleMenu()">
                  <mat-icon mat-list-icon>business</mat-icon>Maintenance
                  <mat-icon *ngIf="!showMenu">chevron_right</mat-icon>
                  <mat-icon *ngIf="showMenu">expand_more</mat-icon>
                </a>
                <mat-nav-list class="sidenav-submenu" *ngIf="showMenu">
                  <a mat-list-item  routerLink="/new-entry"> New Report</a>
                  <a mat-list-item > New User</a>
                  <a mat-list-item > New Business</a>
                </mat-nav-list>
                <a mat-list-item >  
                      <mat-icon> account_circle</mat-icon>
                      <span> Login </span>
                  </a>
      </mat-nav-list>

  </mat-sidenav>

</mat-sidenav-container>

Upvotes: 0

Related Questions