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.