Angular Material: Sidebar Open on Swipe
Tags: angular, material, typescript, sidebar
Published: 2020-03-29 09:13:30

---
title: Angular Material: Sidebar Open on Swipe
published: true
description: Angular material has very good list of components. In this article I am going to show demo of side navigation bar. In desktop it behaves normal when open or close by clicking on navigation button, But in mobile devices or touch-devices often side navigation open by swiping left to right.
tags: #angular, #material, #typescript, #sidebar
---
Angular material provides material sidebar navigation component out of the box.

**First install @angular/material** to your project using below commnad.
        
    ng add @angular/material

# Part 1 

Then after, first add SidenavModule to app.module.ts file.

    import {BrowserAnimationsModule } from '@angular/platform-browser/animations';
    import {MatSidenavModule} from '@angular/material/sidenav';
    @NgModule({
      imports: [BrowserAnimationModule, MatSidenavModule ]
    })

and after that open your project and add below container to app.component.html

    <mat-sidenav-container class="example-container">
       <mat-sidenav mode = "side" opened>            
          <mat-toolbar color="accent">
             <span> Shop Smart </span>
          </mat-toolbar>
       </mat-sidenav>
       <mat-sidenav-content>
          <mat-toolbar color="warn"> Main Toolbar </mat-toolbar>
          <app-container></app-container>
       </mat-sidenav-content>
    </mat-sidenav-container>

here...
 
**1. `<mat-sidenav-container>`** is now main container of our app. everything rest is child of this container or rather we can say everything is wrapped into this `<mat-sidenav-container>`.

**2. `<mat-sidenav>`** is main sidebar navigation component usually open by clicking hamburger icon on mostly top-left or top-right corner of page. 
there are many options needed to change behavior of this sidebar.
    -- mode = "mode" : used to define opening behavior of sidebar on-screen.
           where... 
        
    mode = "over" ( default behavior ). sidebar opens with backdrop and appears over the rest of document.
    mode = "side". sidebar opens in left side and all other document are strength to right side of the sidebar, and no back-drops are shown.
    mode = "push". when sidebar opens content of documents are pushed back to left and right accordingly open and close status sidebar.

**Upto now, we have done as shown in below image...**

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/26o0xypdvb7pl2bpzy9e.jpg)

# Part 2

**Upto now, we have setup basic sidebar in angular app. Now we are going to implement next step of how to open and close sidebar by clicking on hamburger menu.**

`now its time to add more material module to app.module.ts file` 

    import {MatButtonModule} from '@angular/material/button';
    import {MatIconModule} from '@angular/material/icon';
       ***
         imports:      [ MatButtonModule, MatIconModule ],
       ***

**next in your app.component.html in mat-sidebar-content change mat-toolbar...**

    <mat-toolbar color="warn">
        <button mat-mini-fab color="primary" >
          <mat-icon> menu</mat-icon>
       </button>
        <p style="margin-left: 10px;"> Main Toolbar </p> 
    </mat-toolbar>

**next, for properly displayed icon add below code to index.html**

    <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/f8jmz0rpb7528e91vgt3.jpg)

Okay now, sidebar is opened by clicking on menu-button.

1. add template reference variable named `#sidenav` to mat-sidenav tag..

        <mat-sidenav #sidenav > </mat-sidenav>

2. change `mode` to `over`..

        <mat-sidenav #sidenav mode="over"> </mat-sidenav>

3. add click listener to button.. and use `#sidenav` reference variable to open sidebar by clicking on button. mode='over' sets back-drop so by clicking on back-drop sidebar is closed if opened.

        <button mat-mini-fab color="primary" (click)="sidenav.open()" >
           <mat-icon> menu</mat-icon>
        </button>

upto now complete working demo you can find here in this `stackblitz`.

{% stackblitz angular-wno8m4 view=preview %}

# Part 3

In part 3, we are going to add swipe to left open sidebar feature. In order to accomplish this task there are three document events going to be useful. all events are only works for touch-screen devices.. and, These three events are..

    1. touchstart
    2. touchmove
    3. touchend

To done this feature, angular `directive` is very useful. Basically you can use directive in angular whenever there are some extra feature you want to implement. So that, component file is neat and clean and you can reuse directive logic everywhere without re-creating same feature again and again.

**lets create new directive by angular cli...**

    ng g d open-sidebar-onswipe

**Open directive.ts file**

    export class OpenSidebarOnSwipeDirective {
       @HostBinding('style.width') width;
       @Input('sideNav') set sideNav(sideNav: MatSidenav){
          this.sidebar = sideNav;
       }

       @Output() setWidth: EventEmitter<number> = new EventEmitter<number>();

    constructor() { 
    }
    ngAfterViewInit(){
       this.setWidth.emit(80);
       this.startOnTouch();
    }
    startOnTouch(){
       fromEvent(document, 'touchstart').pipe(
       tap((e: TouchEvent) => e.touches[0].clientX <=20 && e.touches[0].clientY >= 65 ? (this.sidebar.open(),
          this.setWidth.emit (e.touches[0].clientX)) : '' )
       ).subscribe();
    }
    }

**Now, add this directive to app.component.html into the mat-sidenav.**

    <mat-sidenav [ngStyle]="{'width': width  +'%' }"
         [sideNav] = "sidenav" (setWidth) = "setWidth ($event) "
         appOpenSidebarOnSwipe>
    </mat-sidenav> 

**Now Open app.component.ts and add below code.**

    export class AppComponent  {
      width: number;
      constructor(private cdr: ChangeDetectorRef){}

      setWidth(widthNumber: number){
        this.width = widthNumber;
        this.cdr.detectChanges();
      }

`ChangeDetectorRef is zone.js angular implementation of change-detection of dom to binding value. by explicitly, `this.cdr.detectChanges()` we tell angular something has changed so update dom value accordingly. 

we use `@Input()` and `@Output()` with directive. Because of directive is special kind of component without template file. so that every feature of component is available in directive same as in component file. 

`[sideNav] = "sidenav" (setWidth) = "setWidth($event)"` , in this line of mat-sidenav template file, firstly we pass `sidebar` template variable to directive and then directive emits event and we get value from directive in component file using `(setWidth)` and this called our `setWidth($event)` and pass value using $event.

{% stackblitz angular-dz2qbs %}

 above, stackblitz link open preview in chrome mobile mode, and then click on side bar you can see sidebar is open where you clicked in. OnStartTouch event is fired on document only when we clicked on side bar screen. this can be done using rxjs `fromEvent()`. this way we can subscribe() to future event when user swipe to left side of screen. Now we improve this..

**now update directive.ts**

      ngAfterViewInit(){
        this.setWidth.emit(80);
        this.startOnTouch();
        this.startTouchMove();
        this.touchEnd();
      }
      startOnTouch(){
        fromEvent(document, 'touchstart').pipe(
        tap((e: TouchEvent) => e.touches[0].clientX <=20 && e.touches[0].clientY >= 65 ? (this.sidebar.open(),
        this.startTime = new Date().getTime(), 
                this.startX = e.touches[0].clientX ,
          this.setWidth.emit (e.touches[0].clientX)) : '' )
          ).subscribe();
       }
       startTouchMove(){
          fromEvent(document, 'touchmove').pipe(
            debounceTime(0)).subscribe(
              (e:TouchEvent) => {
                this.endTime = new Date().getTime();
                let speed = Math.abs(e.touches[0].clientX - this.startX) / (this.endTime - this.startTime);
                this.sidebar._width > 40 ? this.setWidth.emit(80) : '';
                let w = this.sidebar._width;
                this.sidebar._width <= 79 ? this.setWidth.emit ( w += (0.5 + speed)) : '';
              })
     }
     touchEnd(){
       fromEvent(document, 'touchend').subscribe(()=> {
         this.sidebar._width < 40 ? this.sidebar.close() : '';
       } );
    }

**final code is in this stackblitz link**

{% stackblitz angular-ibkdnv %}

open this in chrome mobile mode, and see `onTouch` sidebar is opened and then swiping from left to right sidebar is opened fully. if we leave swipe then sidebar is closed too.