Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Controling the angular material mat-drawer from another component

I have two components, the AppNav component and the Machines component. The AppNav component has the toolbar and sidenav. I have also placed a filter button on the toolbar of the AppNav. In the Machines Component there's a mat-drawer which needs to be toggled after clicking on the filter button in the toolbar of the AppNav. The problem is I don't know how to make the two communicate. The Machines component is not a direct child of the AppNav component since I'm loading the other components (Machines, etc) via router-outlet placed in the sidenav-content of the AppNav.

Look at the components from below:

The DrawerService (that I thought of creating to maybe ease my frustration)

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

@Injectable({
    providedIn: 'root'
})
export class DrawerService {
    private drawer: MatDrawer;

    /**
     * setDrawer
     */
    public setDrawer(flyout: MatDrawer) {
        this.drawer = flyout;
    }

    /**
     * open
     */
    public open() {
        return this.drawer.open();
    }

    /**
     * close
     */
    public close() {
        return this.drawer.close();
    }

    /**
     * toggle
     */
    public toggle(): void {
        this.drawer.toggle();
    }
}

The Machines component (machines.component.ts)

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

@Component({
    selector: 'app-machines',
    templateUrl: './machines.component.html',
    styleUrls: ['./machines.component.sass']
})
export class Machines implements OnInit {

    constructor() { }

    ngOnInit() {
    }

}

The Machines html (machines.component.html)

<mat-drawer-container
    fxFlexFill>
    <mat-drawer 
        mode="side"
        opened
        position="end"
        #flyout
        class="mat-elevation-z3"
        [style.width]="'200px'">

        <mat-toolbar>
            <span>Filter</span>

            <span fxFlex></span>

            <mat-icon 
                svgIcon="close"
                (click)="flyout.toggle()"></mat-icon>
        </mat-toolbar>

        <mat-list>
            <mat-list-item>01</mat-list-item>
            <mat-list-item>02</mat-list-item>
            <mat-list-item>03</mat-list-item>
            <mat-list-item>04</mat-list-item>
        </mat-list>
    </mat-drawer>
    <mat-drawer-content>
        <div>
            Lorem ipsum, dolor sit amet consectetur adipisicing elit. Saepe, id assumenda ratione quaerat at itaque dolorem sit ipsam, modi nam voluptatem nisi expedita quis possimus ea a sed neque? Iure!
        </div>
    </mat-drawer-content>
</mat-drawer-container>

The AppNav component (app-nav.component.ts)

import { Component, ViewChild, ViewChildren } from '@angular/core';
import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { ThemeService } from '../services/theme.service';
import { DrawerService } from '../services/drawer.service';
import { MatDrawer } from '@angular/material';

@Component({
    selector: 'app-nav',
    templateUrl: './nav.component.html',
    styleUrls: ['./nav.component.sass']
})
export class AppNav {
    yearTodate: number = new Date().getFullYear();
    isDarkTheme: Observable<boolean>;
    links: Array<Object>;

    isHandset$: Observable<boolean> = this.breakpointObserver.observe(Breakpoints.Handset)
    .pipe(
        map(result => result.matches)
    );

    @ViewChildren('flyout') public flyout: MatDrawer;

    constructor(
        private breakpointObserver: BreakpointObserver,
        private drawerService: DrawerService,
        private themeService: ThemeService) {}

    ngOnInit() {
        this.isDarkTheme = this.themeService.isDarkTheme;
        this.links = this.linksArray;
        this.drawerService.setDrawer(this.flyout);
    }

    toggleDarkTheme(checked: boolean) {
        this.themeService.setDarkTheme(checked);
    }

    toggleFlyout () {
        this.drawerService.toggle();
    }

    linksArray = [

        {
            route: "/dashboard",
            icon: "ballot",
            name: "Dashboard"
        },

        {
            route: "/vehicles",
            icon: "car",
            name: "Vehicles"
        }
    ]

}

The AppNav html (app-nav.component.html)

<div
    [ngClass]="{'dark-theme': isDarkTheme | async}"
    fxLayout="column" 
    fxLayoutAlign="center center"
    fxFlexFill
    class="mat-app-background"
    style="position: relative">
    <mat-sidenav-container class="sidenav-container">
        <mat-sidenav
            #drawer
            class="sidenav mat-elevation-z3"
            fixedInViewport="true"
            [attr.role]="(isHandset$ | async) ? 'dialog' : 'navigation'"
            [mode]="(isHandset$ | async) ? 'over' : 'side'"
            [opened]="!(isHandset$ | async)"
            fxLayout="column">

            <mat-nav-list>
                <a 
                    mat-list-item
                    routerLink="{{link.route}}"
                    *ngFor="let link of links">
                    <mat-icon svgIcon="{{link.icon}}"></mat-icon>
                    <span fxFlex="5"></span>
                    <span>{{link.name}}</span> 
                </a>
            </mat-nav-list>

            <span fxFlex></span>

            <mat-nav-list>
                <a 
                    mat-list-item
                    routerLink="/login">
                    <mat-icon svgIcon="login"></mat-icon>
                    <span fxFlex="5"></span>
                    <span>Login</span> 
                </a>
            </mat-nav-list>

            <mat-divider></mat-divider>

            <small 
                style="padding: 16px">
                &copy;{{yearTodate}} MotionPlus
            </small>
        </mat-sidenav>

        <mat-sidenav-content
            fxLayout="column">
            <mat-toolbar 
                color="primary">
                <button
                    type="button"
                    aria-label="Toggle sidenav"
                    mat-icon-button
                    (click)="drawer.toggle()"
                    *ngIf="isHandset$ | async">
                    <mat-icon 
                        aria-label="Side nav toggle icon"
                        svgIcon="menu">
                        <!-- menu -->
                    </mat-icon>
                </button>

                <span>MotionPlus</span>

                <span fxFlex></span>

                <button 
                    mat-icon-button
                    (click)="toggleFlyout()">
                    <mat-icon svgIcon="filter-variant"></mat-icon>
                </button>

                <button 
                    mat-icon-button
                    [matMenuTriggerFor]="bucket">
                    <mat-icon
                        svgIcon="format-color-fill"></mat-icon>
                </button>

                <mat-menu
                    #bucket="matMenu">
                    <button 
                        mat-menu-item
                        (click)="toggleDarkTheme(true)"
                        fxLayout="column"
                        fxLayoutAlign="center center">
                        <span
                            style="
                                background: purple; 
                                width: 42px; 
                                height: 42px; 
                                border-radius: 50%">
                        </span>
                    </button>

                    <button 
                        mat-menu-item
                        (click)="toggleDarkTheme(false)"
                        fxLayout="column"
                        fxLayoutAlign="center center">
                        <span
                            style="
                                background: yellow; 
                                width: 42px; 
                                height: 42px; 
                                border-radius: 50%">
                        </span>
                    </button>
                </mat-menu>
            </mat-toolbar>
            <!-- Add Content Here -->
            <div
                style="padding: 0 24px;"
                fxFlex>
                <router-outlet></router-outlet>
            </div>
        </mat-sidenav-content>
    </mat-sidenav-container>
</div>

Point me in the right direction on how I can achieve this. I want to control the opening and closing of the mat-drawer that is in the Machines component (which loads dynamically via router-outlet) from the AppNav component.

like image 953
Kingsley Avatar asked Nov 18 '18 03:11

Kingsley


1 Answers

The component where drawer exists

export class DashboardComponent implements OnInit {

    @ViewChild('drawer') public drawer: MatDrawer;

    ngOnInit() {
        this.toolbarService.setDrawer(this.drawer);
    }

}

Component with the toggle button. This is a child of the component with the drawer:

export class PageToolbarComponent implements OnInit {

    toggleDrawer() {
        this.toolbarService.toggle();
    }

}

Shared service:

private drawer: MatDrawer;

setDrawer(drawer: MatDrawer) {
    this.drawer = drawer;
}

toggle(): void {
    this.drawer.toggle();
}

This worked for me. For more details

like image 110
Shafeeq Mohammed Avatar answered Nov 15 '22 04:11

Shafeeq Mohammed