CanActivate and CanDeAactivate in Angular

In this tutorials we shall see how to guard the route url and provide access permission to the route url.

What are Gaurds?

Guards are used in angular to protect any routes. If the guard returns true, then the navigation is allowed to proceed further else the navigation will be blocked. To make the service as a guard, we need to implement canActivate interface.

CanActivate Gaurd

Let's start with an example program. First create an angular application using

ng new angular-app

Then enter into the project using

cd angular-app

Create a service named UserService to get and set the login status

ng g s services/user

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class UserService {

  private isLoggedIn: boolean;
  constructor() {
    this.isLoggedIn = false;
  }

  getUserLoginStatus(): boolean {
    return this.isLoggedIn;
  }

  setUserLoginStatus(status: boolean) {
    this.isLoggedIn = status;
  }
}

We create a variable named isLoggedIn and initially set it to false in the constructor.
We create a function named getUserLoginStatus which returns isLoggedIn variable and setUserLoginStatus is used to set the value to isLoggedIn variable.

Create a gaurd

Now lets create a gaurd using the below command in 'gaurds' folder

ng g g guards/auth

Select canActivate option by clicking space and press enter. Modify the code as below.

import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs';
import { UserService } from '../services/user.service';

@Injectable({
  providedIn: 'root'
})
export class AuthGuard implements CanActivate {
  constructor(private userService: UserService) { }
  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
    return this.userService.getUserLoginStatus();
  }
}

Here implementing the CanActivate interface will create a canActivate() function with 2 parameters ActivatedRouteSnapshot and RouterStateSnapshot and return a boolean value.

The return value can be an Observable or a Promise returning boolean value or the exact boolean value.

Inject the UserService into the constructor and call the getUserLoginStatus() function of UserService in canActivate() function.
It return true or false based on the isLoggedIn variable's value.

Setup the login status in AppComponent

In, app.component.ts file, we shall modify the code as

import { Component, OnInit } from '@angular/core';
import { UserService } from './services/user.service';
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent implements OnInit {

  isLoggedIn: boolean;
  title = 'Welcome to Readers Buddy canActivate tutorial';
  constructor(private userService: UserService) {
  }
  ngOnInit(): void {
    this.isLoggedIn = this.userService.getUserLoginStatus();
  }

  toggleLogin() {
    this.isLoggedIn = !this.isLoggedIn;
    this.userService.setUserLoginStatus(this.isLoggedIn);
  }
}

Here, we inject the UserService to the constructor to get and set the value of isLoggedIn variable. Create a variable isLoggedIn to get and set its status from getUserLoginStatus() of UserService in ngOnInit() life cyle event.

The toggleLogin() function is used to toggle the variable isLoggedIn and also set isLoggedIn variable value in UserService using setUserLoginStatus() function.

In app.component.html file modify the code as below

<button (click)="toggleLogin()">
    {{ isLoggedIn === true ? "Allow":"Block"}}
</button><br />
<a [routerLink]="['/home']">Home</a> 

<router-outlet></router-outlet>

Here we have a button, whose label value changes based on isLoggedIn variable value. This is done by calling toggleLogin() function on the button click.

Create a HomeComponent and set the gaurd to the component. Create the HomeComponent using the following command.

ng g c home

Setup the gaurd for the HomeComponent

Let's set the route and the guard to the component in app-routing.module.ts file.

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { AuthGuard } from './guards/auth.guard';
import { HomeComponent } from './home/home.component';

const routes: Routes = [
  { path: 'home', component: HomeComponent, canActivate: [AuthGuard] },
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

Run you application using

ng serve --o

In the output screen, when the button text is Allow then it would allow to route and if its in blocked state, then it will not allow to navigate to HomeComponent.

CanDeAactivate

What is CanDeactivate?

CanDeAactivate is a gaurd, that is used to decided whether the route can be deactivated. If it returns true then it would allow to leave the page else it will stay in the same page.

Create gaurd for CanDeactivate

Let's create a DeActivate gaurd in gaurds folder with filename auth-deactivate.guard and add code as below.

import { Injectable } from '@angular/core';
import { CanDeactivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs';
import { HomeComponent } from '../home/home.component';
@Injectable({
  providedIn: 'root'
})
export class CanDeactivateGuard implements CanDeactivate<HomeComponent> {
  canDeactivate(component: HomeComponent, currentRoute: ActivatedRouteSnapshot,
    currentState: RouterStateSnapshot, nextState?: RouterStateSnapshot): boolean | Observable<boolean> | Promise<boolean> {
    return component.canDeactivate();
  }
}

In the above code, we have set the CanDeAactivate type to HomeComponent. The canDeactivate() function in addition takes a component parameter. The component parameter will hold the instance of HomeComponent. So we can call any function inside the component except private functions.

Add canDeactivate() function to HomeComponent

Let's create a function named canDeactivate() in HomeComponent which returns an Observable of boolean type. The function would pop-up a confirm dialog box to the user with message Are you sure to leave the page?. Based on the option selected, a boolean value will be returned as observable.

Modify home.component.ts file with the following code

import { Component, OnInit } from '@angular/core';
import { of, Observable } from 'rxjs';

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {

  constructor() { }

  ngOnInit() {
  }

  canDeactivate(): Observable<boolean> {
    const confirm = window.confirm('Are you sure to leave the page?');
    return of(confirm);
  }
}

So the finally updated code will be as above.

Set the CanDeActivate Gaurd to HomeComponent

Now we have to set the gaurd to the component in app-routing.module.ts file.

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { AuthGuard } from './guards/auth.guard';
import { CanDeactivateGuard } from './guards/auth-deactivate.guard';
import { HomeComponent } from './home/home.component';

const routes: Routes = [
  { path: 'home', component: HomeComponent, canActivate: [AuthGuard], canDeActivate:[CanDeactivateGuard] },
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

Now when we try to navigate away from HomeComponent, it will pop-up a confirm dialog. If Ok is pressed, it will allow to navigate away from the page, else it stays back in the page.


Most Read