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