Angular Routing Guards: Understanding canActivate Guard (Part-2)

Reading Time: 7 minutes

If you are trying to block some routes from loading based on some permissions or blocking a route based if not authenticated, then you can read along and at the end, you will understand about the canActivate Guard.

In the series of Angular Routing Guards Part-1, we’ve learned and understood the basics of angular route guards. If you’ve no prior knowledge about Angular Route Guards, go and refer here first. It helps you to have a clear understanding of them.

Now let’s start to learn something new about one of the routing guards: canActivate. We will learn what is the canActivate guard is and how to use it to protect the route.

What is canActivate guard?

The canActivate guard checks if the user can visit the specific route or we have to prevent access to that specific route. We use the this guard when we have to check some condition and based on that users have the access to reach that specific route or not, before activating the component or showing it to the user. This allows us to prevent navigation.

Basic use cases required for the canActivate guard:

1. Checking if a user has logged in:

  • If they’re not logged in, thus the guard will redirect to the register/login page

2. Checking if a user has permission

  • It checks if the user have rights to access that page or not.

Now, let’s learn how we can use the canActivate guard in our application:

For this, we need to create the Angular service and this service should import and implement the canActivate interface.

The signature of the canActivate looks like this:

interface CanActivate {
  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree
}

The method includes two parameters ActivatedRouteSnapsht & RouterStateSnapshot. We can use this to get access to the route parameter, query parameter, etc.

This guard has multiple return types, i.e., UrlTree or a boolean value with Observables or Promise.

A route can have more than one canActivate guard.

There are several possibilities by which navigation is decided either it will navigate or not which are as follows:

1. If all the guards return true, so the navigation to the route will continue.

2. If any one of the guards returns false, so the navigation will be canceled.

3. If any of the guards return a UrlTree, current navigation will be canceled and new navigation will be kicked off to the UrlTree returned from guard.

In the auth-guard.service.ts, we create a class AuthGuard. The file looks like this:

import {CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router} from '@angular/router';
import { Observable } from 'rxjs/Observable';
export class AuthGuard implements CanActivate {
constructor(private authService: AuthService, private router: Router) {}
canActivate(route: ActivatedRouteSnapshot, state:RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
  // return true if you want to navigate, otherwise return false
 }
}
Now in the app-routing.module.ts, we have to use this service like this:
{ path: 'p/:id', component: UserComponent, canActivate : [AuthGuard] }

Also, don’t forget to add the created service to the providers array in the app.module.ts file e.g.

providers: [ AuthGuard]

Now let’s understand canActivate guard with the help of the example.

In this example, we’ll create three components. The HomeComponent & the SupportComponent are not protected and they can be accessed by any user. The user must have to log in to access the ProductComponent. For handling, the login of the user, we also required the LoginComponent.

Let’s start with the LoginComponent

In the login.component.ts file, we’ve write it as:

import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
import { Router, ActivatedRoute } from '@angular/router';
import { AuthService } from './auth.service';
 
@Component({
   templateUrl: './login.component.html',
   styles: [``]
})
export class LoginComponent implements OnInit { 
 
    invalidCredMsg: string;
    username:string;
    password:string;
    retUrl:string="home";
 
    constructor(private authService: AuthService, 
                private router: Router, 
                private activatedRoute:ActivatedRoute) {
    }
 
    ngOnInit() {
        this.activatedRoute.queryParamMap
                .subscribe(params => {
            this.retUrl = params.get('retUrl'); 
            console.log( 'LoginComponent/ngOnInit '+ this.retUrl);
        });
    }
 
    onFormSubmit(loginForm) {
       this.authService.login(loginForm.value.username, loginForm.value.password).subscribe(data => {
           console.log( 'return to '+ this.retUrl);
           if (this.retUrl!=null) {
                this.router.navigate( [this.retUrl]);
           } else {
                this.router.navigate( ['home']);
           }
       });
    }
} 

The login.component.html will look like this:

<h3>Login Form</h3>
 
<div>
 <form #loginForm="ngForm" (ngSubmit)="onFormSubmit(loginForm)">
   <p>User Name: <input type='text'  name='username' ngModel></p>
   <p>Password: <input type="password"  name="password" ngModel></p>
   <p><button type="submit">Submit</button></p> 
 </form>
</div> 

The auth.service.ts will look like this:

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/map';
import { of } from 'rxjs';
 
@Injectable()
export class AuthService { 
 
    private isloggedIn: boolean;
    private userName:string;
 
    constructor() {
        this.isloggedIn=false;
    }
 
    login(username: string, password:string) {
 
        //Assuming users are provided the correct credentials.
        this.isloggedIn=true;
        this.userName=username;
        return of(this.isloggedIn);
    }
 
    isUserLoggedIn(): boolean {
        return this.isloggedIn;
    }
 
    isAdminUser():boolean {
        if (this.userName=='Admin') {
            return true; 
        }
        return false;
    }
    
    logoutUser(): void{
        this.isloggedIn = false;
    }
 
}

The AuthService checks whether the user has the right to log in. It has the method to log in & log out the users.

Note: Our implementation of the login method does not check for anything. It just marks the user as logged in.

ProductComponent

After login, the user can access the ProductComponent. And it is our protected component. Only logged-in users can access this. This component displays the list of users, which it gets from the ProductService.

The product.component.ts will look like this:

import { Component, OnInit } from '@angular/core';
import { ProductService } from './product.service';
import { Product } from './Product';
 
 
@Component({
  templateUrl: "product.component.html",
})
export class ProductComponent
{
 
   products:Product[];
   constructor(private roductService: productService){
   }
 
   ngOnInit() {
 
      this.productService.getProducts()
        .subscribe(data => {
          this.products=data;
        })
   }
  
}

The product.component.html will look like this:

<h1>Product List</h1>
   <p> This is a protected component </p>
 
   <div class='table-responsive'>
   <table class='table'>
       <thead>
           <tr>
               <th>Name</th>
               <th>Price</th>
           </tr>
       </thead>
       <tbody>
           <tr *ngFor="let product of products;">
               <td><a>{{product.name}} </a> </td>
               <td>{{product.price}}</td>
           </tr>
       </tbody>
     </table>
  </div>

The product.service.ts will look like this:

import {Product} from './Product'
import { of, Observable, throwError} from 'rxjs';
import { delay, map } from 'rxjs/internal/operators';
 
export class ProductService{
 
    products: Product[];
    
    public constructor() {
        this.products=[
            new Product(1,'Memory Card',500),
            new Product(2,'Pen Drive',750),
            new Product(3,'Power Bank',100),
            new Product(4,'Computer',100),
            new Product(5,'Laptop',100),
            new Product(6,'Printer',100),
        ]
    }
 
    public getProducts(): Observable<Product[]> {
        return of(this.products) ;
    }
 
    public getProduct(id): Observable<Product> {
        var Product= this.products.find(i => i.productID==id)
        return of(Product) ;
    }
 
}

The product.ts will look like this:

export class Product { 
 
    constructor(productID:number,    name: string ,   price:number) {
        this.productID=productID;
        this.name=name;
        this.price=price;
    }
 
    productID:number ;
    name: string ;
    price:number;
 
}

Now let’s go through the main components file i.e., app.component.ts. It will looks like this:

import { Component } from '@angular/core';
import { AuthService } from './auth.service';
import { Router } from '@angular/router';
 
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})
export class AppComponent {
  title = 'Routing Module - CanACtivate Route Guards Demo';
 
  constructor (private authService:AuthService, 
               private router:Router) {
  }
  logout() {
    this.authService.logoutUser();
    this.router.navigate(['home']);
  }
}

The app.component.html file will look like this:

<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]="['product']">Product</a></li>
        <li><a [routerLink]="['contact']">Contact us</a></li>
        <li><a [routerLink]="['login']">Login</a></li>
        <li><a [routerLink]="" (click)="logout()">Log out</a></li>
        
    </ul>
  </div>
</nav>
 
 <router-outlet></router-outlet>
 
</div>

Now for better understanding, let’s define the rest of the components.

The home.component.ts file will be like this:

import {Component} from '@angular/core';
 
@Component({
    template: `<h1>Welcome!</h1>
              <p>This is Home Component </p>
             `
})
 
export class HomeComponent {
}

The contact.component.ts file will be like this:

import {Component} from '@angular/core';
 
@Component({
     template: `<h1>Contact Us</h1>
                <p>Knoldus Software</p>
                `
})
export class ContactComponent {
}

Now the most important part of this blog is to implement the canActiavte guard in this example. Here, we’ll check whether the users are logged in or not. If the user is no logged in then the user can’t access the product page and they will be redirected to the login page.

For that first, we have to make our auth-guard.service.ts file like this:

import { Injectable } from '@angular/core';
import { Router, CanActivate, ActivatedRouteSnapshot,RouterStateSnapshot, UrlTree } from '@angular/router';
import { AuthService } from './auth.service';
 
 
@Injectable()
export class AuthGuardService implements CanActivate {
 
    constructor(private router:Router, private authService: AuthService ) {
 
    }
 
    canActivate(route: ActivatedRouteSnapshot,
                state: RouterStateSnapshot): boolean|UrlTree {
 
        if (!this.authService.isUserLoggedIn()) {
            alert('You are not allowed to view this page. You are redirected to login Page');
            
            this.router.navigate(["login"],{ queryParams: { retUrl: route.url} });
            return false;
        } 
 
        return true;
    }
}

The important points to note here is:

1) Here, first we’ve imported the canActivate guard from the @angular/router module

2) The AuthGuardService implements the canActivate interface.

3) Inject the AuthService in the constructor of the guard.

4) In the canActiavte method, we will redirect the user to the login page, if the user is not logged in. To cancel the navigation, we must either return false.

Next, we’ve to updates the routes and have to use the guards in all the routes, which we want to protect.

The app.routes.ts file will look like this:

import { Routes } from '@angular/router';
 
import { HomeComponent} from './home.component'
import { ContactComponent} from './contact.component'
import { ProductComponent} from './product.component'
 
import { AuthGuardService } from './auth-guard.service';
import { LoginComponent } from './login.component';
 
 
export const appRoutes: Routes = [
  { path: 'home', component: HomeComponent },
  { path: 'login', component:LoginComponent},
  { path: 'contact', component: ContactComponent },
  { path: 'product', component: ProductComponent, canActivate : [AuthGuardService] },
  { path: '', redirectTo: 'home', pathMatch: 'full' },
];

At last, finally, we’ve to register the service in 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 { ProductComponent} from './product.component'
 
import { AuthGuardService } from './auth-guard.service';
 
import { appRoutes } from './app.routes';
import { AuthService } from './auth.service';
import { LoginComponent } from './login.component';
import { ProductService } from './product.service';
 
@NgModule({
  declarations: [
    AppComponent,HomeComponent,ContactComponent,ProductComponent,LoginComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    RouterModule.forRoot(appRoutes)
  ],
  providers: [AuthGuardService,AuthService, ProductService],
  bootstrap: [AppComponent]
})
export class AppModule { }
Run the app. You can access the product page only if you log in as shown in the image below.

Conclusion:

So, in this blog, we’ve learned about canActivate route guard provided by angular that allow us to protect access to client-side routes but as said nothing, on client-side is completely protected. Any asset or data delivered to the client is completely accessible by them. Hence, always be sure to protect sensitive data on the server.

Hey there, I am glad you have reached the end of this post. If you liked this post or have some questions or want to discuss something let me know in the comment section. And stay tuned for the more implementation of canActivateChild Angular Router Guard in the coming part of the series.

For more info you can check:

https://blog.knoldus.com/routing-and-navigation-detail-angular2/

https://angular.io/guide/router-reference

https://blog.knoldus.com/angular-routing-guards-lets-understand-route-guards-part-1/

knoldus-advt-sticker

Written by 

Alka Vats is a Software Consultant at Knoldus. She is passionate about web development. She is recognized as a good team player, a dedicated and responsible professional, and a technology enthusiast. She is a quick learner & curious to learn new technologies. Her hobbies include reading books, watching movies, and traveling.

Leave a Reply