Table of Contents
Angular is a great framework which has everything you need to develop enterprise level applications. Route guards are one of the important features for any single page applications, which helps you to stop the user from being able to navigate to restricted routes. Recently, I was asked to implement this in an angular application by one of our clients. Gladly, that helped us to accumulate more insights in building secured applications in many regards. So, here I would like to confess about the real-world implementations of route guards to all of you. On the go, we will see the different type of route guards available and how we can write and use them.
What Is Route Guard In Angular?
Basically, a route guard is a function which returns either a boolean or a boolean promise or a boolean observable, which tells the router whether to load the next route or not. In Angular 7.1+ you can also return a UrlTree to redirect the user to a different page. If you return false, the navigation will be canceled and the user can not access the next route or page he is trying to access.
Using Route Guards For Authentication
We can use route guards for various things like blocking the user from unauthorized access, role-based authentication. Today I am going to show you a simple example of how to create an auth guard. Also, will have a deep insight on route guard which prevents the user from navigating away from a page if he has unsaved changes.
Before that let’s get a quick dive into different kind of route guards available and the order of execution for route guards. This is important to know because route guards can be implemented at multiple levels and even can be applied on lazily loaded modules.
Note: There is one important thing you must know, security cannot be implemented in the UI code. Your API is responsible for securing the data. Route guards are rather nice additions on the UI which provides a way to control the navigation flow of the app. They are not security enforcers in any way.
Types Of Guards In Angular
CanActivate – To decide whether to activate a route
CanActivateChild – To decide whether to activate any child route under the current route
CanDeactivate – To decide whether to allow the user to navigate away from the current route
CanLoad – To decide whether to download a lazy loadable module
Resolve – To perform data retrieval before a particular route loads
The router primarily has two cycles of checking the route guards, one is upwards from the deepest route to the top route. In this cycle, it checks the CanDeactivate and CanActivateChild guards. In the second cycle, it checks the CanActivate guard beginning from the topmost route and goes towards the deepest route. At any point, if one guard returns false, the rest of the guards will not be checked and the navigation event will be canceled.
For the said tasks, we are going to use canActivate, CanLoad, and CanDeactivate guards. We had enough talk, So, let’s dive into the code.
What Is Auth Guard In Angular
Auth guard is nothing but a special type of guard which prevents the user from accessing unauthorized areas of our application. We are creating an auth guard for normal and lazy loaded routes below.
Creating Auth Guard In Angular
Create a new file and paste the below code in it, save the file as auth.guard.ts
import {Injectable} from '@angular/core'; import {ActivatedRoute, ActivatedRouteSnapshot, CanActivate, CanLoad, Route, Router, RouterStateSnapshot} from '@angular/router'; @Injectable() export class AuthGuard implements CanActivate, CanLoad { constructor(private router: Router) { } canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { return this.checkToken(); } canLoad(route: Route): boolean { return this.checkToken(); } checkToken() { if (localStorage.getItem('token')) { return true; } else { this.router.navigateByUrl('/user/login'); return false; } } }
Set up the above guard in your protected routes and alter the checkToken function as necessary.
Best To Read: How To Prevent Memory Leaks In Angular Observables
To implement the route guard which prevents the user from navigating away from a page if he has unsaved changes, please follow the steps.
Creating Interface In Angular
First, let’s create an interface for the components which are going to use this route guard.
For that, create a new file and paste the below code then you can save it as “canExit.ts”
import {Observable} from 'rxjs'; export interface CanExit { canDeactivate: () => Observable<boolean> | Promise<boolean> | boolean; }
The above interface is to be implemented by the components which should implement the canDeactivate method. And then they can handle their deactivation like a boss.
Creating Route Guard In Angular
Create a new file and paste the below code & finally save it as “can-exit.guard.ts”
import {Injectable} from '@angular/core'; import {CanDeactivate} from '@angular/router'; import {CanExit} from '@types/canExit'; @Injectable() export class CanExitGuard implements CanDeactivate<CanExit> { canDeactivate(component: CanExit) { if (component.canDeactivate) { return component.canDeactivate(); } return true; } }
What we are doing above is when a route deactivation is triggered. We are checking the current component and checking if there is a canDeactivate method. And, if present we are calling it and delegating the navigation control to the component itself. The component can then just display a popup and ask the user if they want to navigate away based on its own state. Let’s see a simple implementation of a component which uses sweetalert2.
Also read: How To Build Login Page Using Angular Material Design Clearly Explained
Creating Component In Angular
Now we are into the final step, to make it work, we must create a component & link everything together. So, go ahead start creating a new file and paste the below code & finally save it as “update-feedback.component.ts”.
import { Component, OnInit } from '@angular/core'; import { FormControl, FormGroup, Validators } from '@angular/forms'; import { CanExit } from '@types/canExit'; import swal from 'sweetalert2'; @Component({ selector: 'app-feedback-update, templateUrl: './update-feedback.component.html', styleUrls: ['./update-feedback.component.less'] }) export class UpdateFeedbackComponent implements OnInit, CanExit { public form: FormGroup; ngOnInit() { this.form = this.fb.group({ 'name': new FormControl({ value: null, disabled: true }, Validators.required), 'description': new FormControl({ value: null, disabled: true }) }); } canDeactivate(): Promise<any> | boolean { if (this.form.dirty) { return new Promise((resolve, reject) => { swal({ text: 'You want to discard the changes you made?', }).then(result => { if (result.value) { resolve(true); } else { resolve(false); } }); }); } else { return true; } } }
This is a bare bone version of the component when you edit the form and try to navigate away. It’ll ask you a confirmation and based on your choice you’ll either navigated away or the navigation event will be canceled. at the end, you’ll stay in the current screen.
Setting Up Routing In Angular
Finally, in your routing file, create a route definition and add the canDeactivate guard to that route to see everything in action.
{ path: 'update-feedback', component: UpdateFeedbackComponent, canDeactivate: [CanExitGuard], }
We’ve seen how route guards work & their order of execution. Finally, we implemented a reusable guard which prevents the user from losing unsaved changes. Feel free to get in touch for any feedback and queries, I am eager to hear your different use cases with the route guards.
[contact-form-7 404 "Not Found"]