CanDeactivate Guard In Angular

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
}

We must first establish an Angular Service.

As the first argument, the method receives the instance of the deactivated component.

The method also returns the ActivatedRouteSnapshot instance, the RouterStateSnapshot's current state, and the RouterStateSnapshot's next state. This can be used to access the route parameter, query parameter, and so on.

True/false or an UrlTree must be returned by the guard. The return value can be an observable, a promise, or just a basic boolean value.

There can be many canDeactivate guards on a route.

The component will be deactivated and you will be directed to the next route if all guards return true.

If any of the guards return false, navigation is aborted and you remain in the same route/component.

CanDeactivate Example

Create a component with a canExit method that returns a boolean. You can use the canExit method to see if there is any unsaved data, for example. If the user says yes, confirm whether or not they want to leave. If you want to depart the component, return true; otherwise, return false to stay in the same component.

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] },

The RegisterComponent guard created in the preceding example is unique. We can use the interface to develop a general-purpose DeactivateGuard Service that we can use wherever. 

General Purpose DeactivateGuard Service

First, create an IDeactivateComponent interface as shown below.

export interface IDeactivateComponent {
    canExit: () => Observable<boolean> | Promise<boolean> | boolean;
}

After that, open the component where you want to use Deactivate Guard. It's RegisterComponent in our case. As demonstrated below, make the component implement the IDeactivateComponent interface. Implement the method canExit.

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;
  }
 
}

Example of CanDeactivate Guard In Angular

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
    }
  }
  
}

Conclusion

We covered how to use CanDeactivate Guard in this article. This guard is meant to prevent the user from leaving the path by accident before saving their work.

I hope this article helps you and you will like it.👍

If you have any doubt or confusion then free to ask in the comment section.

Post a Comment

Previous Post Next Post