Angular Material: Sidebar Open on Swipe
GaurangDhorda
Posted on March 29, 2020
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...
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">
Okay now, sidebar is opened by clicking on menu-button.
-
add template reference variable named
#sidenav
to mat-sidenav tag..<mat-sidenav #sidenav > </mat-sidenav>
-
change
mode
toover
..<mat-sidenav #sidenav mode="over"> </mat-sidenav>
-
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
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.
Posted on March 29, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.