Búsqueda de sitios web

angular - Abrir/Cerrar sidenav desde otro componente


Uso Angular (última versión) y Angular Material.

Hay 3 componentes:

  • header.component, en el que hay un botón de control para el side-nav derecho
  • rigth-sidenav.component, en el cual está el right-sidenav
  • sidenav.component (este es el menú principal izquierdo), este componente llama a header.component, right-sidenav.component y content

¿Cómo abrir/cerrar el sidenav desde otro componente? (en mi caso, el botón está en header.component).

Intenté la siguiente opción (pero obtuve el error: TypeError: this.RightSidenavComponent.rightSidenav no está definido):

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() { }

}

Muestra que no funciona con código en Stackblitz

Respuesta #1

Tuve el mismo problema al usarlo. Lo resolví de esta manera.

ServicioDeBarraLateral

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();
   }

Tu Componente

constructor(
private sidenav: SidenavService) { }

toggleRightSidenav() {
   this.sidenav.toggle();
}

Vincula tu toggle() de HTML según tu requerimiento.

Componente de la aplicación.

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

constructor(private sidenavService: SidenavService) {
}

ngAfterViewInit(): void {
  this.sidenavService.setSidenav(this.sidenav);
}

Módulo de la aplicación

providers: [YourServices , SidenavService],

Ejemplo funcional con código en Stackblitz

Actualización de Angular 9+

Según esta respuesta en SO, "Los componentes ya no se pueden importar a través de @angular/material. Utilice los puntos de entrada secundarios individuales, como @angular/material/button.". Por lo tanto, asegúrese de importar MatSidenav de la siguiente manera:

import { MatSidenav } from '@angular/material/sidenav';

Respuesta #2

Usé @Input() inputSideNav: MatSideNav en el padre\ otro componente para pasar el objeto sideNav como propiedad de destino desde el hijo. Funciona como se esperaba. Por cierto, me gustó la implementación del servicio por parte de @Eldho :)

  1. Es sencillo integrarlo en tu implementación actual
  2. Es escalable a tantos sideNav u otros controles de UI pasados como referencia en la definición del componente
  3. Sin desorden en el código y tan simple como parece.
  4. Vota a favor si entiendes lo que queremos decir :)

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() {
  }
}

Artículos relacionados