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
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.
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.
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.
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
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
{ path: 'product', component: ProductComponent, canActivate : [AuthGuardService] },
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.
The following is the code for LoginComponent and associated AuthService [tabby title=”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']);
}
});
}
}
<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>
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 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.
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”]
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;
})
}
}
<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>
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) ;
}
}
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;
}
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']);
}
}
<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>
import {Component} from '@angular/core';
@Component({
template: `<h1>Welcome!</h1>
<p>This is Home Component </p>
`
})
export class HomeComponent {
}
import {Component} from '@angular/core';
@Component({
template: `<h1>Contact Us</h1>
<p>TekTutorialsHub </p>
`
})
export class ContactComponent {
}
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”]
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 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”]
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, register the service in the app.module.
[tabby title=”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 { }
Run the app. You can access the Product page only if you login as shown in the image below.