DEV Community

GaurangDhorda
GaurangDhorda

Posted on

Angular Material: Sidebar Open on Swipe

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
Enter fullscreen mode Exit fullscreen mode

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 ]
})
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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.
Enter fullscreen mode Exit fullscreen mode

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

Alt Text

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 ],
   ***
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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

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

Alt Text

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.

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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();
}
}
Enter fullscreen mode Exit fullscreen mode

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> 
Enter fullscreen mode Exit fullscreen mode

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();
  }
Enter fullscreen mode Exit fullscreen mode

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() : '';
   } );
}
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (0)