Controlling Navigation with CanDeactivate Guard

Posted By : Satwinder Singh | 30-Apr-2018

In Angular, as we use the CanActivate Guard to check if we can access a route or not, we can also check whether the user is allowed to leave a route or not by using the CanDeactivate Guard. Let’s suppose we are logged in and we are allowed to fill some type of form and we have some changes is that form which is contained in a particular component and user try to leave that component or leave that route or maybe the user accidentally clicks back and we want to prevent the user from leaving the page or at least ask him if he really want to leave that page by displaying some dialog box or alert box, so in this case we can use the CanDeactivate Guard to prevent the user from accidentally navigating away.

 

Implementing CanDeactivate Guard

 

For implementing the CanDeactivate Guard let us take a very simple example of a form having some input fields and a button to submit the form or maybe to save the changes done in the form lying in a component.html file. In the component.ts file, we are using onSubmit() method which is triggered when the button is clicked. Now we want to use a new property changes saved which is false by default and set to true when we click the button or we call the onSubmit() method. After the changes have been saved we want to navigate away which we will do by injecting the router in the constructor and using the navigate() method. Now let's make sure that when the user accidentally tries to navigate away, we prevent him from doing so or at least asks the user if he wants to leave that page. Now we somehow need to execute this code in this component as we need to access to the changes saved property to check whether the user clicked the save/submit button or not. After this the component.ts file will look like:

 

import {Component, OnInit} from @angular/core;
import {ActivatedRoute, Params} from @angular/router;

@Component({
    //here goes the component declarations
})

export class Component implements OnInt{
    changesSaved = false;

    constructor( )
    onSubmit(){
         this.changesSaved = true;
    }
}  
        

However, a Guard always needs to be a service so we will create a new file named can-deactivate-guard.service.ts file where we will first of all export an interface i.e. CanComponentDeactivate interface and this interface will require one thing from the component which implements this interface and the component should have a canDeactivate() method. This canDeactivate() method must return an Observable or a Promise resolving to a boolean value or it may simply return a boolean in the end. Now our interface is constructed and we can work on the service itself by exporting CanDeactivateGuard class that will implement the CanDeactivate interface(provided by angular router) which will wrap our CanComponentDeactivate interface and this setup will make sure that we connect our component to the canDeactivate guard. Now, this class also need to have a canDeactivate() method which will be called by the angular router once we try to leave a route. This canDeactivate() method receive the following arguments:

  • The first argument will be the component on which we are currently on which will be of type CanComponentDeactivate.
  • It will also receive the current route as an argument which is of type ActivatedRouteSnapshot
  • It will have the current state which will be of type RouterStateSnapshot as a third argument.
  • The last argument will be the nextState which is optional and also of type RouterStateSnapshot.

Now this canDeactivate() will return a Promise or an Observable or a boolean in the end. Inside this method, we will call the canDeactivate() method on the component we are currently on and this is why we created the interface in the first place. After this our can-deactivate-guard.service, ts file will look like this:

import {Observable} from ‘rxjs/Observable’;
import {CanDeactivate , ActivatedRoueSnapshot, RouterStateSnapshot} from ‘@angular/router’;

export interface CanComponentDeactivate{
    canDeactivate: () => Obseravble | Promise | boolean;
}

export class CanDeactivateGuard implements CanDeactivate{
    canDeactivate( component: CanComponentDeactivate,
        currentRoute: ActivatedRouteSnapshot,
        currentState: RouterStateSnapshot,
        nextState: RouterStateSnapshot): Observable | Promise | boolean {
    return component.canDeactivate();
    }
}                

Now we can go to our app-routing module and add canDeactivate as a property to our route config which will be an array and we can point this property to our CanDeactivateGuard(make sure to add the import to the path at the top) and now angular will run this guard whenever we try to leave this path or leave the component loaded at this path. We also need to provide our CanDeactivateGuard in the provider's array in the app.module.ts file. app-routing.module.ts file:-

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { CanDeactivateGuard } from './can-deactivate-guard.service';
const appRoutes: Routes = [
     { path: '', component: HomeComponent ,canDeactivate: [CanDeactivateGuard]},
     // here goes other routes in this array of routes
]
@NgModule({
     imports: [
         RouterModule.forRoot(appRoutes)
     ],
exports: [RouterModule]
})
export class AppRoutingModule {}              

Now for the CanDeactivateGuard to work, we need to implement the CanComponentDeactivate interface and also call a canDeactivate() method in our component.ts file which will contain the logic to check whether we are allowed to leave or not. Our final component.ts file will look like:

   import {Component, OnInit} from @angular/core;
import {ActivatedRoute, Params} from @angular/router;

@Component({
    //here goes the component declarations
})

export class Component implements OnInt{
    changesSaved = false;

    constructor( )
    onSubmit(){
         this.changesSaved = true;
    }
    canDeactivate(): Observable | Promise | boolean{
        If(!this.changesSaved){
        return confirm(‘Do you want to discard the changes?’);
    }
    else{
        return true;
    }
}
}             

About Author

Author Image
Satwinder Singh

Satwinder had been working as a free-lancer for past 1-year and recently joined Oodles Technologies as a UI-Developer. His skills are : Jquery , Html , Css , Bootstrap and Javascript.

Request for Proposal

Name is required

Comment is required

Sending message..