htmlangulartypescriptangular-materialsidenav

Angular Material: How to toggle sidenav when the trigger is in a different component


how do I toggle the sidenav from another component?

When the button/trigger is in the same .html file as the sidenav, it work just fine, but if I generate a new component, in my case I named them "sidebar-left" and "topbar," it don't work.

What I did is I generated components to separate them. I put the sidenav in the "sidebar-left" component and the button/trigger in "topbar" component.

sidebar-left.component.html

<mat-sidenav-container>

    <mat-sidenav #sidenav mode="side" [(opened)]="opened" opened>
        <h3>Sidenav</h3>
    </mat-sidenav>

    <mat-sidenav-content>
        <app-dashboard></app-dashboard>
    </mat-sidenav-content>

</mat-sidenav-container>

topbar.component.html

<mat-toolbar>
    <button (click)="sidenav.toggle()" mat-raised-button>Toggle Sidenav</button>
</mat-toolbar>

I would like to be able to trigger the sidenav no matter where I trigger it inside the application.

And when I do hit the toggle button, I'm having this error:

TopbarComponent.html:1 ERROR TypeError: Cannot read property 'toggle' of undefined
    at Object.eval [as handleEvent] (TopbarComponent.html:2)
    at handleEvent (core.js:43993)
    at callWithDebugContext (core.js:45632)
    at Object.debugHandleEvent [as handleEvent] (core.js:45247)
    at dispatchEvent (core.js:29804)
    at core.js:42925
    at HTMLButtonElement.<anonymous> (platform-browser.js:2668)
    at ZoneDelegate.invokeTask (zone-evergreen.js:391)
    at Object.onInvokeTask (core.js:39680)
    at ZoneDelegate.invokeTask (zone-evergreen.js:390)

Here is my not-working StackBlitz code


Solution

  • There are three posible ways to communicate between components. The error you are getting is because there is not sidevav property in your component and no toggle function is declared on an undefined member.

    There are specific ways you can communicate between components.

    Use the native event driven communication.

    Parent to child through input (value), child to parent through output (event).

      //this is how to create an event emitter
      @Output() toggle: EventEmitter<any> = new EventEmitter();
    
      constructor() { }
    
      emit() {
          this.toggle.emit(null);
      }
    

    and in the ts file you create the toggle function

    toggle() {
         this.firstSelected = !this.firstSelected;
    }
    

    Then you pass the first selected value to your components and hide unhide accordingly.

    This is the official way. Check this solution working on stackblitz here

    Use an event service

    You can create an event service from which you can raise events and the toggle master can subscribe to them and toggle application wide. check for example here

    Use a libary to share state via stores

    Check documentation here