CanActivate Guard In Angular

CanActivate Guard In Angular

Before we browse to a route, the Angular CanActivate guard runs, allowing us to abort the navigation. In this article, we will learn what is CanActivate guard and how to apply it to safeguard the route. To demonstrate how to use CanActivate in a real-world application, we'll create a simple CanActivate Example app.

What is CanActivate Guard

If a route can be activated, the Angular CanActivate guard decides ( or component gets rendered). When we wish to check on a condition before activating or showing a component to the user, we use this guard. This enables us to halt the navigation process.

Use cases of CanActivate Guard

  • checking user has logged in or not
  • checking user has authorization or not

Checking if the user has logged in to the system is one of the guard's applications. If the user has not yet logged in, the guard can send him to the login page.

How to use CanActivate Guard

We must first establish an Angular Service.

The CanActivate Interface must be imported and implemented by the service. The @angular/router module defines this interface. canActivate is the only technique available in the Interface. It's something we'll have to work on in our Service. The CanActivate interface is described in detail below.

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

The method returns the ActivatedRouteSnapshot and RouterStateSnapshot instances. This can be used to access the route parameter, query parameter, and so on.

True/false or an UrlTree must be returned by the guard. The return value can be an observable, a promise, or just a basic boolean value.

There can be multiple canActivate guards on a route.

The navigation to the route will continue if all guards return true.

Navigation will be canceled if any of the guards return false.

If any of the guards return an UrlTree, the current navigation will be terminated and new navigation to the UrlTree returned by the guard will begin.

The following is an example of a canActivate guard.

import { Injectable } from '@angular/core';
import { Router, CanActivate, ActivatedRouteSnapshot,RouterStateSnapshot } from '@angular/router';
 
 
@Injectable()
export class AuthGuardService implements CanActivate {
 
    constructor(private _router:Router ) {
    }
 
    canActivate(route: ActivatedRouteSnapshot,
                state: RouterStateSnapshot): boolean {
 
        //check some condition  
        if (someCondition)  {
            alert('You are not allowed to view this page');
            //redirect to login/home page etc
            //return false to cancel the navigation
            return false;
        } 
        return true;
    }
 
}

As seen below, add the canActivate guard to the route definition. More than one guard can be applied to a route, and a route can have several guards.

{ path: 'product', component: ProductComponent, canActivate : [AuthGuardService] },

CanActivate guard Example

Three components will be created in our example application. The HomeComponent and ContactComponent are not password-protected, and any user can access them. To access the ProductComponent, the user must first log into the system. To manage user login, we'll additionally require a LoginComponent.

LoginComponent

The code for LoginComponent and its associated AuthService is as follows.

Add the following code under the login.component.ts:

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 { 
 
    invalidCredentialMsg: 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']);
           }
       });
    }
} 

Now, Add the following code under the login.component.html:

<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> 

Update the auth.service.ts using the following code:

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.
        //In real app you will query the database to verify.
        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 determines whether or not the user is permitted to log in. It has a technique for logging in and out users. The login procedure in our implementation does not perform any checks. It just indicates that the user is logged in.

ProductComponent

Our protected component is the ProductComponent. This is only accessible to logged-in users. The ProductService returns a list of Products to this component, which it displays.

Add the following code under the product.component.ts:

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 productService: ProductService){
   }
 
   ngOnInit() {
 
      this.productService.getProducts()
        .subscribe(data => {
          this.products=data;
        })
   }
  
}

Add the following code under the product.component.html:

<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>

Add the following code under the product.service.ts:

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) ;
    }
 
}

Add the following code under the product.ts:

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;
 
}

Other Component

Add the following code under the app.component.ts:

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 - Route Guards Demo';
 
  constructor (private authService:AuthService, 
               private router:Router) {
  }
 
  logout() {
    this.authService.logoutUser();
    this.router.navigate(['home']);
  }
 
}

Add the following code under the app.component.html:

<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>

Add the following code under the home.component.ts:

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

contact.component.ts
import {Component} from '@angular/core';
 
@Component({
     template: `<h1>Contact Us</h1>
                <p>TekarticlesHub </p>
                `
})
export class ContactComponent {
}

CanActivate Guard

Finally, we create a CanActivate guard that checks whether or not the users are logged in. Users are forwarded to the login page if they are not logged in.

Add the following code under the auth-guard.service.ts:

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;
 
            //var urlTree = this.router.createUrlTree(['login']);
            //return urlTree;
        } 
 
        return true;
    }
 
}

First, we import the @angular/router module's CanActivate.

The CanActivate interface is implemented by the AuthGuardService.

Inject the AuthServce into the Guard's constructor.

If the user is not logged in, the CanActivate method will redirect them to the login page. We must either return false or urlTree to abort the navigation, as seen in the example above.

After that, we'll edit the route definition and apply the guard to all of the routes we wish to protect.

Add the following code under the app.routes.ts:

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' },
];

Finally, in the app.module, register the service.

Add the following code under the app.module.ts:

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 { }

Start the app. Only by logging in as seen in the image below can you view the Product page.

Conclusion

In this article, we learned what is CanActivate guard and how to apply it to safeguard the route.

I hope this article helps you and you will like it.👍

If you have any doubt or confusion then free to ask in the comment section.

Post a Comment

Previous Post Next Post