The Angular CanActivate guard runs before we navigate to a route allowing us to cancel the navigation. In this tutorial, we will learn what is CanActivate
guard is and how to use it to protect the route. We will build a simple CanActivate
Example app to show you how to use it in real application
Table of Contents
What is CanActivate Guard
The Angular CanActivate guard decides, if a route
can be activated ( or component gets rendered). We use this guard, when we want to check on some condition, before activating the component or showing it to the user. This allows us to cancel the navigation.
Use cases for the CanActivate Guard
- Checking if a user has logged in
- Checking if a user has permission
One of the use case of this guard is to check if the user has logged in to the system. If user has not logged in, then the guard can redirect him to login page.
How to use CanActivate Guard
First, we need to create a Angular Service.
The service must import & implement the CanActivate
Interface. This Interface is defined in the @angular/router
module. The Interface has one method i.e. canActivate
. We need to implement it in our Service. The details of the CanActivate
interface is as shown below.
1 2 3 4 5 | interface CanActivate { canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree } |
The method gets the instance of the ActivatedRouteSnapshot
& RouterStateSnapshot
. We can use this to get access to the route parameter, query parameter etc.
The guard must return true/false or a UrlTree . The return value can be in the form of observable or a promise or a simple boolean
value.
A route can have more than one canActivate
guard.
If all guards returns true
, navigation to the route will continue.
If any one of the guard returns false
, navigation will be cancelled.
If any one of the guard returns a UrlTree
, current navigation will be cancelled and a new navigation will be kicked off to the UrlTree
returned from the guard.
The example of canActivate
guard is as follows
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | 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; } } |
Update the route definition with the canActivate
guard as shown below. You can apply more than one guard to a route and a route can have more than one guard
1 2 3 | { path: 'product', component: ProductComponent, canActivate : [AuthGuardService] }, |
CanActivate guard Example
In our example application, we will create three components. The HomeComponent
& ContactComponent
are not protected and can be accessed any user. The user must log in into the system to access the ProductComponent
..We also need a LoginComponent
to handle the user login.
LoginComponent
The following is the code for LoginComponent
and associated AuthService
[tabby title=”login.component.ts”]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | 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']); } }); } } |
[tabby title=”login.component.html”]
1 2 3 4 5 6 7 8 9 10 11 | <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> |
[tabby title=”auth.service.ts”]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | 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; } } |
[tabbyending]
The AuthService
checks whether the user is allowed to login. It has the method to login
& logout
the users. Our implementation of the login
method does not check for anything. It just marks the user as logged in.
ProductComponent
The ProductComponent
is our protected component. Only the logged in users can access this. This component displays the list of Products, which it gets from the ProductService
.
[tabby title=”product.component.ts”]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | 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; }) } } |
[tabby title=”product.component.html”]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <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> |
[tabby title=”product.service.ts”]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | 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) ; } } |
[tabby title=”product.ts”]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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; } |
[tabbyending]
Other Component
[tabby title=”app.component.ts”]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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']); } } |
[tabby title=”app.component.html”]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <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> |
[tabby title=”home.component.ts”]
1 2 3 4 5 6 7 8 9 10 11 12 | import {Component} from '@angular/core'; @Component({ template: `<h1>Welcome!</h1> <p>This is Home Component </p> ` }) export class HomeComponent { } |
[tabby title=”contact.component.ts”]
1 2 3 4 5 6 7 8 9 10 11 | import {Component} from '@angular/core'; @Component({ template: `<h1>Contact Us</h1> <p>TekTutorialsHub </p> ` }) export class ContactComponent { } |
[tabbyending]
CanActivate Guard
Finally, we build a CanActivate
guard, which will check whether the users are logged in or not. If users are not logged in, then they are redirected to the login page.
[tabby title=”auth-guard.service.ts”]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | 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; } } |
[tabbyending]
First, we import the CanActivate
from the @angular/router
module.
The AuthGuardService
implements the CanActivate
interface
Inject the AuthServce
in the constructor of the Guard
In the CanActivate
method, we will redirect the user the login page, if the user is not logged in. To cancel the navigation ,we must either return false
or urlTree
as shown in the example above.
Next, we will update the route definition and use the guard in all the routes, which we want to protect
[tabby title=”app.routes.ts”]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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' }, ]; |
[tabbyending]
Finally, register the service in the app.module
.
[tabby title=”app.module.ts”]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | 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 { } |
[tabbyending]
Run the app. You can access the Product page only if you login as shown in the image below.
CanActivate is deprecated.
Use CanActivateFn
how?
Hi..
can we get sitename on pop up. instead of localhost says — and message ?
create custom model Popups
Hi,
I am facing issue like cannot access inside canActivate() method I am not able to see console values. initialized authguardservice in providers while debug found that its not calling inside canActivate method can you please help in this issue.
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
console.log(“check”)
return true;
}
Hi,
I am facing this issue like in authguardservice file inside canActivate method console is not printing. I have added authguardservice in providers…
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
console.log(“check”)
return true;
}
How to use “invalidCredentialMsg” to check whether user entered incorrect username and password?
Hi,
How do I apply this algorithm to hide or show component without navigate anywhere. In the other words, I have index page and 2 components. User will be validated in “A” component. If validation is pass, He will be seeing “B” component… should I just modify this line: { path: ‘product’, component: ProductComponent, canActivate : [AuthGuardService] },
Thank you
Add AuthGuardService to B Component.
Hello if I want to give access module wise then how to do so?
I mean to say if you have component A ,component B and component C
And I have user harry which has access rights of A and B.
Then I have jack who has access rights of B and C
How can we achieve that?
Setup a role for each component. You can use route data to store role [ Refer Pass data to route]
Load the user role when user logs in.
Match the user role with the component role and in the CanActivate Guard.
Google “Role Based Authorization in Angular”, you will get several articles
why u write logout function in app.component.ts instead of login component?
The logout button is in the app.component.html. Hence the code is in the app.component.ts.