CanDeactivate Guard In Angular
The Angular CanDeactivate Guard determines whether or not we can leave a route. In this article, we will learn what CanDeactivate Guard is and how to use it with a basic CanDeactivate example application.
What is CanDeactivate Guard
When we navigate away from the route before the current component is deactivated, the Angular CanDeactivate guard is called.
The data entry component is the ideal use case for CanDectivate guard. It's possible that the user has completed the data entering and is attempting to exit the component without saving his work. We may use the CanDeactivate guard to alert the user that he hasn't saved his work and provide him the option to abort the navigation.
How to use CanDeactivate Guard
We must first establish an Angular Service.
The CanDeactivate Interface must be imported and implemented by the service. The @angular/router module defines this interface. canDeactivate is the only way available in the Interface. It's something we will have to work on in our Service. The CanDeactivate interface's details are shown below.
interface CanDeactivate<T> { canDeactivate(component: T, currentRoute: ActivatedRouteSnapshot, currentState: RouterStateSnapshot, nextState?: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree }
CanDeactivate Example
import { Component } from '@angular/core'; @Component({ templateUrl: "register.component.html", }) export class RegisterComponent //Check if there any unsaved data etc. If yes then as for confirmation canExit() : boolean { if (confirm("Do you wish to Please confirm")) { return true } else { return false } } }
Create a guard service that implements the CanDeactivate Interface after that.
import { Injectable } from '@angular/core'; import { CanDeactivate } from '@angular/router/src/utils/preactivation'; import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; import { Observable } from 'rxjs'; import { RegisterComponent } from './register.component'; @Injectable() export class DeactivateGuard implements CanDeactivate { component: Object; route: ActivatedRouteSnapshot; constructor(){ } canDeactivate(component:RegisterComponent, route: ActivatedRouteSnapshot, state: RouterStateSnapshot, nextState: RouterStateSnapshot) : Observable<boolean> | Promise<boolean> | boolean { return component.canExit(); } }
As seen below, add the canDeactivate guard to the route specification. More than one guard can be applied to a route, and a route can have several guards.
{ path: 'register', component: RegisterComponent, canDeactivate:[DeactivateGuard] },
General Purpose DeactivateGuard Service
export interface IDeactivateComponent { canExit: () => Observable<boolean> | Promise<boolean> | boolean; }
import { Component } from '@angular/core'; import { IDeactivateComponent } from './decativate.guard'; @Component({ templateUrl: "register.component.html", }) export class RegisterComponent implements IDeactivateComponent { //Check if there any unsaved data etc. If yes then as for confirmation canExit() : boolean { if (confirm("Do you wish to Please confirm")) { return true } else { return false } } }
Use the IDeactivateComponent interface instead of the real component in the guard service now.
@Injectable() export class DeactivateGuard implements CanDeactivate { component: Object; route: ActivatedRouteSnapshot; constructor(){ } canDeactivate(component:IDeactivateComponent, route: ActivatedRouteSnapshot, state: RouterStateSnapshot, nextState: RouterStateSnapshot) : Observable<boolean> | Promise<boolean> | boolean { return component.canExit ? component.canExit() : true; } }
The following code is the complete code of the app.component.html:
<div class="container"> <nav class="navbar navbar-default"> <div class="container-fluid"> <div class="navbar-header"> <a class="navbar-brand" [routerLink]="['/']"><strong> {{title}} </strong></a> </div> <ul class="nav navbar-nav"> <li><a [routerLink]="['home']">Home</a></li> <li><a [routerLink]="['register']">Register</a></li> <li><a [routerLink]="['contact']">Contact us</a></li> </ul> </div> </nav> <router-outlet></router-outlet> </div>
The following code is the complete code of the app.component.ts file:
import { Component } from '@angular/core'; import { Router } from '@angular/router'; @Component({ selector: 'app-root', templateUrl: './app.component.html' }) export class AppComponent { title = 'Routing Module - Route Guards Demo'; constructor (private router:Router) { } }
Add the following code under the app.module.ts file:
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { HttpModule } from '@angular/http'; import { FormsModule } from '@angular/forms'; import { RouterModule } from '@angular/router'; import { AppComponent } from './app.component'; import { HomeComponent} from './home.component' import { ContactComponent} from './contact.component' import { appRoutes } from './app.routes'; import { RegisterComponent } from './register.component'; import { DeactivateGuard } from './decativate.guard'; @NgModule({ declarations: [ AppComponent,HomeComponent,ContactComponent,RegisterComponent ], imports: [ BrowserModule, FormsModule, HttpModule, RouterModule.forRoot(appRoutes) ], providers: [DeactivateGuard], bootstrap: [AppComponent] }) export class AppModule { }
Add the following code under the app.routing.ts file.
import { Routes } from '@angular/router'; import { HomeComponent} from './home.component' import { ContactComponent} from './contact.component' import { RegisterComponent } from './register.component'; import { DeactivateGuard } from './decativate.guard'; export const appRoutes: Routes = [ { path: 'home', component: HomeComponent }, { path: 'contact', component: ContactComponent }, { path: 'register', component: RegisterComponent, canDeactivate:[DeactivateGuard] }, { path: '', redirectTo: 'home', pathMatch: 'full' }, ];
Add the following code under the deactivate-guard.ts file:
import { Injectable } from '@angular/core'; import { CanDeactivate } from '@angular/router/src/utils/preactivation'; import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; import { Observable } from 'rxjs'; import { RegisterComponent } from './register.component'; export interface IDeactivateComponent { canExit: () => Observable<boolean> | Promise<boolean> | boolean; } @Injectable() export class DeactivateGuard implements CanDeactivate { component: Object; route: ActivatedRouteSnapshot; constructor(){ } canDeactivate(component:IDeactivateComponent, route: ActivatedRouteSnapshot, state: RouterStateSnapshot, nextState: RouterStateSnapshot) : Observable<boolean> | Promise<boolean> | boolean { return component.canExit ? component.canExit() : true; } }
Add the following code under the contact.component.html file:
import {Component} from '@angular/core'; @Component({ template: `<h1>Contact Us</h1> <p>TekarticlesHub </p> ` }) export class ContactComponent { }
Add the following code under the home.component.html file:
import {Component} from '@angular/core'; @Component({ template: `<h1>Welcome!</h1> <p>This is Home Component </p> ` }) export class HomeComponent { }
Add the following code under the register.component.html file:
import { Component } from '@angular/core'; import { IDeactivateComponent } from './decativate.guard'; @Component({ template: `<h1>Register User</h1> <p> </p>`, }) export class RegisterComponent implements IDeactivateComponent { //Check if there any unsaved data etc. If yes then as for confirmation canExit() : boolean { if (confirm("Do you wish to Please confirm")) { return true } else { return false } } }