There are four different guard types available, and we can use them to protect application routes before they navigate,
- CanActivate # It decides if a route can be activated or not
- CanActivateChild # it decides if child routes of a route can be activated or not
- CanDeactivate # It decides if a route can be deactivated or not
- CanLoad # It decides if a module can be loaded lazily or not
Define Guards
Guards can be implemented in different ways but after all, it returns either Promise<boolean>, Observable<boolean> or boolean. Guards can be registered, using providers and they can be injected by Angular when they needed.
Guards as Functions
We should define a token and the guard function to register a guard. Here is the simple guard implementation, which looks, as shown below.
- ---------
- ---------
- ---------
- @NgModule({
- ---------
- ---------
- ---------
- providers: [canActivateGuard,
- useValue: () => {
- return true;
- }],
- ---------
- ---------
- ---------
- })
- export class AppModule { }
If you observe the block of code given above, it is just a provider with some made up token, which resolves to a guard function that returns true. As this guard is always returning true, so it is not protecting anything, as it always activates the route that uses it.
Once a guard is registered with a token, we can use them in our route configuration. If you observe the block of code given below, the RouteGuard attached with the route configuration, which gets executed when we navigate to a specific route.
- import { Routes } from '@angular/router';
- import { UserListComponent } from './components/user/user.list.component';
- import { NewUserComponent } from './components/user/new.user.component'
- import { TodoComponent } from './components/todo/todo.component';
- import { UserComponent } from './components/user/user.component';
- import { LoginComponent } from './components/login/login.component';
- import { RouteGaurd } from './services/route.gaurd';
- import { NewUserGaurd } from './components/user/new.user.gaurd';
-
- export const APP_ROUTES: Routes = [
- {
- path: '', component: LoginComponent
- },
- {
- path: 'login', component: LoginComponent
- },
- {
- path: 'users',
- canActivate: [RouteGaurd],
- children: [
- {
- path: 'new', component: NewUserComponent,
- canDeactivate:[NewUserGaurd]
- },
- {
- path: '', component: UserListComponent
- }
- ]
- },
- {
- path: 'user/:id', component: UserComponent, canActivate: [RouteGaurd]
- },
- {
- path: 'todos',
- component: TodoComponent , canActivate: [RouteGaurd]
- },
- {
- path: 'todos/:id', component: TodoComponent, canActivate: [RouteGaurd]
- }
- ];
We can have multiple guards to protect a single route. They execute in the order they are defined on the route.
Guards as Classes
Let's say our application needs to validate for an authenticated user before allowing to access the Application. Thus, to protect a route, we have to authenticate the user first. In such a case, guard class will be a perfect selection. To create a guard class, we should implement either CanActivate, CanActivateChild or CanDeactivate interfaces. Based on the interface implementation, we should define the respective methods canActivate(), canActivateChild(), canDeactivate(); they are equivalent as guard function. The block of code snippet given below shows you a simple CanActivate guard class implementation.
CanActivate Guard as a class
- import {Injectable} from '@angular/core';
- import {CanActivate, Router} from '@angular/router'
- import {AuthService} from '../services/auth.service'
-
- @Injectable()
- export class RouteGaurd implements CanActivate{
- constructor(private _authService: AuthService, private route: Router){}
- canActivate(){
- console.log("Gaurd invoked")
- if(this._authService.isAuthenticated())
- {
- console.log("User found:", this._authService.getLoggedInUserData())
- return true;
- }
- console.log("Invalid user!");
- this.route.navigate(['/login']);
- return false;
- }
- }
Registering the above created guard class in app.module.ts file (AppModule class) as a provider.
- ---------
- ---------
- ---------
- @NgModule({
- ---------
- ---------
- ---------
- providers: [AuthService, RouteGaurd]
- })
- export class AppModule { }
Once we register in app.module.ts file (AppModule class) as a provider, then we can use them in a route.
- {
- path: 'todos',
- component: TodoComponent , canActivate: [RouteGaurd]
- },
CanDeactivate Guard as a Class
We have seen the uses of CanActivate class. It is very simple. Before navigating a route, it validates first, followed by allowing it to navigate, whereas CanDeactivate works just a reverse way, before leaving the current route, it gives us an alert to decide, if we really want to navigate away from the current route. This is very useful, when we don't want to lose the filled form data, if accidently clicking on a Cancel button or some navigation buttons.
The implementation of CanDeactivate guard is almost similar as CanDeactivate guard implementation. The sample code snippet screenshot is given below.
- import { Injectable } from '@angular/core';
- import { Routes } from '@angular/router';
- import {CanDeactivate, ActivatedRouteSnapshot, RouterStateSnapshot} from '@angular/router'
- import {NewUserComponent} from './new.user.component';
- import {Observable} from 'rxjs/rx';
-
- @Injectable()
- export class NewUserGaurd implements CanDeactivate<NewUserComponent>{
- constructor(){}
- canDeactivate(component: NewUserComponent,
- router: ActivatedRouteSnapshot,
- state: RouterStateSnapshot):
- Observable<boolean> | Promise<boolean> | boolean{
- console.log("canDeactivate: ", component);
- if(!component.user.firstName){
- return confirm("Please fill the form!");
- }
- return true;
- }
- }
In the code snippet given above, there is one thing that we did not see in the previous code snippet. CanDeactivate<T> uses a generic, so we need to specify what component type; we want to deactivate. Here, honestly even I don't know why the syntax varies compared to CanActivate uses. I am not sure whether this is a bug or not but other than this, the rest of the things are the same as CanActivate syntax.
Registering the above created guard class in app.module.ts file (AppModule class) as a provider.
- ---------
- ---------
- ---------
- @NgModule({
- ---------
- ---------
- ---------
- providers: [ConfirmDeactivateGuard]
- })
- export class AppModule { }
Once we register in app.module.ts file (AppModule class) as a provider, then we can use them in a route.
- {
- path: 'users',
- canActivate: [RouteGaurd],
- children: [
- {
- path: 'new', component: NewUserComponent,
- canDeactivate:[NewUserGaurd]
- },
- {
- path: '', component: UserListComponent
- }
- ]
- },
Output
Screenshot #1 is given below.
Screenshot #2 is given below.
Screenshot #3 is given below.
Conclusion
I love these features because they prompt us to protect from accidentally losing the data; and also, they are very useful to validate the user before they allow us to navigate a particular route. In addition to it, we can use multiple guards to protect a single route.
Happy coding!