Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Open/Close sidenav from another component

I use angular (latest version) and angular material.

There are 3 components:

  • header.component, in which there is a control button for right-sidenav
  • rigth-sidenav.component, in which is the right-sidenav
  • sidenav.component (this is the left main menu), this component calls header.component, right-sidenav.component and content

How to open / close sidenav from another component ? (in my case, the button is in header.component).

Tried the following option (but got the error: TypeError: this.RightSidenavComponent.rightSidenav is undefined):

header.component.html

<button mat-button (click)="toggleRightSidenav()">Toggle side nav</button> 

header.component.ts

import { Component } from '@angular/core'; import { RightSidenavComponent } from '../right-sidenav/right-sidenav.component'; @Component({   selector: 'app-header',   templateUrl: './header.component.html',   styleUrls: ['./header.component.scss'] }) export class HeaderComponent {      constructor(public RightSidenavComponent:RightSidenavComponent) {   }     toggleRightSidenav() {         this.RightSidenavComponent.rightSidenav.toggle();     }  } 

sidenav.component.html

<mat-sidenav-container class="sidenav-container">     <mat-sidenav #sidenav mode="side" opened="true" class="sidenav"                  [fixedInViewport]="true"> Sidenav  </mat-sidenav>     <mat-sidenav-content>         <app-header></app-header>         <router-outlet></router-outlet>         <app-right-sidenav #rSidenav></app-right-sidenav>     </mat-sidenav-content> </mat-sidenav-container> 

sidenav.component.ts

import { Component, Directive, ViewChild } from '@angular/core'; import { RightSidenavComponent } from '../right-sidenav/right-sidenav.component';  @Component({   selector: 'app-sidenav',   templateUrl: './sidenav.component.html',   styleUrls: ['./sidenav.component.scss'] })  export class SidenavComponent {      @ViewChild('rSidenav') public rSidenav;     constructor(public RightSidenavComponent: RightSidenavComponent) {         this.RightSidenavComponent.rightSidenav = this.rSidenav;     }  } 

right-sidenav.component.html

<mat-sidenav #rightSidenav mode="side" opened="true" class="rightSidenav"              [fixedInViewport]="true" [fixedTopGap]="250">     Sidenav </mat-sidenav> 

right-sidenav.component.ts

import { Component, Injectable } from '@angular/core';  @Component({   selector: 'app-right-sidenav',   templateUrl: './right-sidenav.component.html',   styleUrls: ['./right-sidenav.component.scss'] })  @Injectable() export class RightSidenavComponent {      public rightSidenav: any;      constructor() { }  } 

Non-working sample with code stackblitz

like image 766
Denis Avatar asked Jan 03 '18 07:01

Denis


2 Answers

I had the same problem using. I resolved it like this.

SidenavService

import { Injectable } from '@angular/core'; import { MatSidenav } from '@angular/material';  @Injectable() export class SidenavService {     private sidenav: MatSidenav;       public setSidenav(sidenav: MatSidenav) {         this.sidenav = sidenav;     }      public open() {         return this.sidenav.open();     }       public close() {         return this.sidenav.close();     }      public toggle(): void {     this.sidenav.toggle();    } 

Your Component

constructor( private sidenav: SidenavService) { }  toggleRightSidenav() {    this.sidenav.toggle(); } 

Bind your html toggle() based on your requirement.

App component.

@ViewChild('sidenav') public sidenav: MatSidenav;  constructor(private sidenavService: SidenavService) { }  ngAfterViewInit(): void {   this.sidenavService.setSidenav(this.sidenav); } 

App Module

providers: [YourServices , SidenavService], 

Working sample with code stackblitz

Angular 9+ Update

Per this answer on SO, "Components can no longer be imported through @angular/material. Use the individual secondary entry-points, such as @angular/material/button." As such, make sure to import MatSidenav like so:

import { MatSidenav } from '@angular/material/sidenav'; 
like image 74
Eldho Avatar answered Sep 16 '22 20:09

Eldho


I used @Input() inputSideNav: MatSideNav in parent\ another component to pass the sideNav object as target property from child component. It works as expected. By the way, I liked the service implementation by @Eldho :)

  1. It is simple to plugin in your current implementation
  2. It is scalable to as many sideNav or other UI controls passed as reference into the component definition
  3. No cluttering in code and plain as it looks.
  4. Vote up if you get what we mean :)

Child.component.html

<app-layout-header [inputSideNav]="sideNav"></app-layout-header> <mat-sidenav-container>   <mat-sidenav #sideNav (click)="sideNav.toggle()" mode="side">     <a routerLink="/">List Products</a>   </mat-sidenav>   <mat-sidenav-content>     <router-outlet></router-outlet>   </mat-sidenav-content> </mat-sidenav-container> 

layout-header.component.html

<section>   <mat-toolbar>     <span (click)="inputSideNav.toggle()">Menu</span>   </mat-toolbar> </section> 

layout-header.component.ts

import { Component, OnInit, Input } from '@angular/core'; import { MatSidenav } from '@angular/material';  @Component({   selector: 'app-layout-header',   templateUrl: './layout-header.component.html',   styleUrls: ['./layout-header.component.css'] }) export class LayoutHeaderComponent implements OnInit {   @Input() inputSideNav: MatSidenav;    constructor() { }    ngOnInit() {   } } 
like image 25
Ashokan Sivapragasam Avatar answered Sep 17 '22 20:09

Ashokan Sivapragasam