Angular Guards: Controlling Navigation with canDeactivate Guard

Introduction

canDeactivate is a route guard which will execute the logic and determine whether we can navigate out from a route or not. 
Suppose the user navigates to the user creation component page and without saving the details after entering they are trying to leave the page, we can implement canDeactivate guard there. We can show a popup, alert or confirmation box to warn or inform the user that details are not saved. If we show a confirm box, we can even give a chance to the user to save the details before leaving. If we want to show some extra information while leaving any route, we can show using canDeactivate guard. 

canDeactivate Guard

We will create one service which will implement canDeactivate interface which we need to import from the @angular/router module. This service will have @Injectable with it to use it in our application. We will define the method canDeactivate function in our service to implement the interface as follows:


interface CanDeactivate {
  canDeactivate(component: T, 
                currentRoute: ActivatedRouteSnapshot, 
                currentState: RouterStateSnapshot, 
                nextState?: RouterStateSnapshot): Observable | Promise | boolean | UrlTree
}

In this function, the first argument is the component that we want to deactivate. The second argument is the current route which is an instance of ActivatedRouteSnapshot. The other arguments are instances of RouterStateSnapshot which will give information about the current and the next stage of the route. Using these arguments we will have access to route and state. The method will return either synchronously or asynchronous values. It can return an observable or promise asynchronously or a boolean value or UrlTree. If our method returns UrlTree, new navigation will be executed from the UrlTree. The current navigation will be canceled.

  • If canDeactivate method returns true, then the current route will get deactivated and we will navigate to the next route.
  • If canDeactivate method returns false, then we can't navigate. We will be on the same route or component.
  • If it returns UrlTree, then the next navigation will be the UrlTree. It will cancel the current navigation in that case.

Using canDeactivate

First, we will create a service called UserDeactivateGuard that will implement canDeactivate interface. In this service, we will define canDeactivate() method as follows:

import { Injectable } from '@angular/core';
import { canDeactivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs';
import { UserCreateComponent } from './user.component';
 
@Injectable()
export class UserDeactivateGuard implements CanDeactivate 
{

   constructor(){
   }
 
  canDeactivate(component: T, 
                currentRoute: ActivatedRouteSnapshot, 
                currentState: RouterStateSnapshot, 
                nextState?: RouterStateSnapshot): Observable<boolean | urlTree > | Promise<boolean | urlTree> | boolean | UrlTree {
        
        // Our logic here
 
  }
  
}

We will go to user-create.component.ts file and we will define one method whether the user has some unsaved changes or not. If the user is navigating before saving the changes, we will show a confirmation box stating that "You have unsaved changes in the user form. Discard changes?". 

import { Component } from '@angular/core';
 
@Component({
  templateUrl: "user-create.component.html",
})
export class UserCreateComponent
 canDiscard() { 
    if (window.confirm("You have unsaved changes in the user form. Discard changes?")) {
      return true
    } else {
       return false
       }
   }
  
}

Now add the logic in our UserDeactivateGuard service. We will define the first argument component as UserCreateComponent. We will call the isDiscard() method of this component to check whether the user can navigate away from the current route or not. If the user selects Ok in 

import { Injectable } from '@angular/core';
import { canDeactivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs';
import { UserCreateComponent } from './user.component';
 
@Injectable()
export class UserDeactivateGuard implements CanDeactivate 
{

   constructor(){
   }
 
  canDeactivate(component: UserCreateComponent, 
                currentRoute: ActivatedRouteSnapshot, 
                currentState: RouterStateSnapshot, 
                nextState?: RouterStateSnapshot): Observable<boolean | urlTree > | Promise<boolean | urlTree> | boolean | UrlTree {
        
  return component.canDiscard();
 
  }
  

Now we will add our canDeactivate guard in our route configuration.  

{ path: 'user-create', component: UserCreateComponent, canDeactivate:[UserDeactivateGuard] },

Comments