Angular CanLoad Guard Example

By Arvind Rai, August 31, 2021
This page will walk through Angular CanLoad route guard example. The CanLoad guard is used to decide if a module configured with loadChildren property can be loaded or not. The CanLoad is an interface with canLoad method. To use CanLoad guard, we need to create a service by implementing CanLoad interface and override its canLoad method. Then that service is configured with canLoad property in route configuration. When canLoad method of our service returns true then the feature module protected by canLoad will be lazy loaded otherwise it will not be loaded. CanLoad takes precedence on preloading and preloading is blocked by CanLoad guard. Modules protected by CanLoad has no effect of preloading configuration.
Here on this page we will protect a feature module using CanLoad guard and allow to load it only after user authentication. We will also provide example for how to use CanLoad and CanActivate guard together.

Technologies Used

Find the technologies being used in our example.
1. Angular 12.1.0
2. Node.js 12.14.1
3. NPM 7.20.3

CanLoad

CanLoad is a route guard to decide if a module can be loaded configured with loadChildren property. loadChildren property is a reference to lazy loaded child routes. If a feature module is protected with CanLoad and also it is configured for preloading then CanLoad has precedence over preloading and that feature module will not be preloaded.
Find the structure of CanLoad from Angular doc.
interface CanLoad { 
  canLoad(route: Route): Observable<boolean> | Promise<boolean> | boolean
}
We will create a service implementing CanLoad interface and override canLoad method. Now using canLoad property we guard loading of modules. When Angular try to access a module protected by CanLoad either by preloading or lazy loading, then canLoad method of our service will run. If canLoad method of our service returns true then that module will start loading otherwise it will not be loaded. To achieve can load we can also use a function with canLoad method signature.

Steps to use CanLoad Guard

Find the steps to use CanLoad route guard.
Step-1: Create a service implementing CanLoad interface and override canLoad method. Here we will protect module loading for unauthenticated user. We are creating a service AuthGuardService as following.
auth-guard.service.ts
import { Injectable }       from '@angular/core';
import { CanLoad, CanActivate, Route, Router } from '@angular/router';
import { AuthService }      from './authentication/services/auth.service';

@Injectable()
export class AuthGuardService implements CanLoad {
  constructor(private authService: AuthService, private router: Router) {
  }
  canLoad(route: Route): boolean {
    const url: string = route.path;
    console.log('Url:'+ url);
    if (this.authService.isUserLoggedIn()) {
	return true; 
    }
    this.authService.setRedirectUrl(url);
    this.router.navigate([ this.authService.getLoginUrl() ]);
    return false;		
  }
} 
If user is already logged-in, canLoad method will return true in the above code and protected module will be applicable to be loaded by lazy loading. If user is not logged-in, then user will be redirected to login page and canLoad method will return false and protected module will not be loaded even by preloading.

Step-2: Configure AuthGuardService with canLoad property in route configuration as following in application routing module.
{
   path: 'admin',
   loadChildren: () => import('./admin/admin.module').then(mod => mod.AdminModule),
   canLoad: [ AuthGuardService ]
} 
Our application routing module will look like as following.
app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { PreloadAllModules } from '@angular/router';
import { WelcomeComponent }  from './welcome.component';
import { DashboardLayoutComponent }  from './dashboard.layout.component';
import { PageNotFoundComponent }  from './page-not-found.component';
import { AuthGuardService }  from './auth-guard.service';

const routes: Routes = [
  {
    path: '',
    redirectTo: '/welcome',
    pathMatch: 'full'
  },    
  {
    path: '',
    component: DashboardLayoutComponent,
    children: [
      {
        path: 'address',
        loadChildren: () => import('./address/address.module').then(mod => mod.AddressModule)
      },
      {
        path: 'admin',
        loadChildren: () => import('./admin/admin.module').then(mod => mod.AdminModule),
        canLoad: [ AuthGuardService ]
      },
      {
        path: 'welcome',
        component: WelcomeComponent 
      },	      	      
      {
        path: '**',
        component: PageNotFoundComponent 
      }	
    ]
  }  	
];

@NgModule({
  imports: [ 
      RouterModule.forRoot(routes,
      {
        preloadingStrategy: PreloadAllModules
      }) 
  ],
  exports: [ 
      RouterModule 
  ],
  providers: [ AuthGuardService ]
})
export class AppRoutingModule { } 
Step-3: Configure AuthGuardService in providers metadata as following.
@NgModule({
  providers: [ AuthGuardService ],
  ------
}) 
export class AppRoutingModule { } 
Now we are ready with CanLoad route guard. In our route configuration we have configured PreloadAllModules strategy for modules preloading. When the application starts, we will see that AddressModule will be preloaded but AdminModule will not be preloaded because it is protected by CanLoad route guard. When we try to access admin feature, we will be redirected to login page and once we are successfully logged-in, then AdminModule will start loading. If user is logged-out, still user can access the admin feature because AdminModule is already loaded.

CanLoad with CanActivate

CanLoad protects a module to be loaded but once module is loaded then CanLoad guard will do nothing. Suppose we have protected a module loading using CanLoad guard for unauthenticated user. When user is logged-in then that module will be applicable to be loaded and we will be able to navigate children paths configured by that module. But when user is logged-out, still user will be able to navigate those children paths because module is already loaded. In this case if we want to protect children paths from unauthorized users, we also need to use CanActivate guard. As we have already created a service AuthGuardService for CanLoad, we will also add code for CanActivate in it. Find the AuthGuardService class.
auth-guard.service.ts
import { Injectable } from '@angular/core';
import {
  CanLoad, CanActivate, Route, Router,
  ActivatedRouteSnapshot, RouterStateSnapshot
} from '@angular/router';
import { AuthService } from './authentication/services/auth.service';

@Injectable()
export class AuthGuardService implements CanLoad, CanActivate {
  constructor(private authService: AuthService, private router: Router) {
  }
  canLoad(route: Route): boolean {
    const url = route.path;
    console.log('Url:' + url);
    if (this.authService.isUserLoggedIn()) {
      return true;
    }
    this.authService.setRedirectUrl(url);
    this.router.navigate([this.authService.getLoginUrl()]);
    return false;
  }
  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
    const url: string = state.url;
    console.log('Url:' + url);
    if (this.authService.isUserLoggedIn()) {
      return true;
    }
    this.authService.setRedirectUrl(url);
    this.router.navigate([this.authService.getLoginUrl()]);
    return false;
  }
} 
In AppRoutingModule we have already configured AuthGuardService with canLoad property as given below.
{
   path: 'admin',
   loadChildren: () => import('./admin/admin.module').then(mod => mod.AdminModule),
   canLoad: [ AuthGuardService ]
} 
Now we will configure AuthGuardService with canActivate property in AdminRoutingModule that is routing module of feature module AdminModule. Find the code.
admin-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AdminComponent }  from './admin.component';
import { PersonListComponent }  from './person-list/person.list.component';
import { AuthGuardService }  from '../auth-guard.service';

const personRoutes: Routes = [
   { 
      path: '',
      component: AdminComponent,
      children: [ 
       {
         path: 'person-list',
         component: PersonListComponent,
         canActivate: [ AuthGuardService ]
       }
      ]
    }  
];

@NgModule({
  imports: [ RouterModule.forChild(personRoutes) ],
  exports: [ RouterModule ]
})
export class AdminRoutingModule {  

Project Structure of Demo Application

Find the project structure of demo application.
my-app
|
|--src
|   |
|   |--app 
|   |   |
|   |   |--authentication
|   |   |    | 
|   |   |    |--services
|   |   |    |    |
|   |   |    |    |--auth.service.ts
|   |   |    |    |--user.ts
|   |   |    |
|   |   |    |--auth.module.ts
|   |   |    |--auth-routing.module.ts 
|   |   |    |--login.component.css
|   |   |    |--login.component.html
|   |   |    |--login.component.ts
|   |   |    |--logout.component.ts
|   |   |
|   |   |--admin
|   |   |    | 
|   |   |    |--admin.component.ts
|   |   |    |--admin-routing.module.ts
|   |   |    |--admin.module.ts
|   |   |    |--person.ts
|   |   |    | 
|   |   |    |--person-list 
|   |   |    |    |
|   |   |    |    |--person.list.component.html 
|   |   |    |    |--person.list.component.ts
|   |   |    |    |
|   |   |    | 
|   |   |    |--services
|   |   |    |    |  
|   |   |    |    |--person.service.ts
|   |   |
|   |   |--address
|   |   |    |
|   |   |    |--address.component.ts
|   |   |    |--address-routing.module.ts
|   |   |    |--address.module.ts
|   |   |
|   |   |--auth-guard.service.ts
|   |   |
|   |   |--dashboard.layout.component.ts
|   |   |--welcome.component.ts
|   |   |--page-not-found.component.ts
|   |   |--app.component.ts
|   |   |--app-routing.module.ts
|   |   |--app.module.ts
|   |   
|   |--main.ts
|   |--index.html
|   |--styles.css
|
|--node_modules
|--package.json 


Complete Example


1. Code for Authentication Feature

auth.service.ts
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import { map } from 'rxjs/operators';

import { User } from './user';

const USERS = [
	new User(1, 'mahesh', 'm123', 'ADMIN'),
	new User(2, 'krishna', 'k123', 'ADMIN')
];
let usersObservable = of(USERS);

@Injectable()
export class AuthService {
	private redirectUrl: string | undefined = '/';
	private loginUrl: string = '/login';
	private isloggedIn: boolean = false;
	private loggedInUser: User | null = null;
	getAllUsers(): Observable<User[]> {
		return usersObservable;
	}
	isUserAuthenticated(username: string, password: string): Observable<boolean> {
		return this.getAllUsers().pipe(
			map((users: User[]) => {
				let user = users.find(user => (user.username === username) && (user.password === password));
				if (user) {
					this.isloggedIn = true;
					this.loggedInUser = user;
				} else {
					this.isloggedIn = false;
				}
				return this.isloggedIn;
			}));
	}
	isUserLoggedIn(): boolean {
		return this.isloggedIn;
	}
	getRedirectUrl(): string | undefined {
		return this.redirectUrl;
	}
	setRedirectUrl(url: string | undefined): void {
		this.redirectUrl = url;
	}
	getLoginUrl(): string {
		return this.loginUrl;
	}
	getLoggedInUser(): User | null {
		return this.loggedInUser;
	}
	logoutUser(): void {
		this.isloggedIn = false;
	}
} 
user.ts
export class User { 
  constructor(public userId:number, public username:string, public password:string, public role:string) {
  }
} 
auth.module.ts
import { NgModule }   from '@angular/core';
import { CommonModule }   from '@angular/common';
import { ReactiveFormsModule }    from '@angular/forms';
import { LoginComponent }  from './login.component';
import { AuthRoutingModule }  from './auth-routing.module';
import { AuthService }  from './services/auth.service';

@NgModule({
  imports: [     
      CommonModule,
      ReactiveFormsModule,
      AuthRoutingModule
  ], 
  declarations: [
      LoginComponent
  ],
  providers: [ AuthService ]
})
export class AuthModule { } 
auth-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

import { LoginComponent }  from './login.component';

const authRoutes: Routes = [
	{ 
	  path: 'login',
	  component: LoginComponent
	}
];

@NgModule({
  imports: [ RouterModule.forChild(authRoutes) ],
  exports: [ RouterModule ]
})
export class AuthRoutingModule{ } 
login.component.css
:host { 
   position: absolute; 
   background-color: #eaedf2;
   top: 10%;
   left: 5%;
   border: 3px solid black;
} 
login.component.html
<h3>Login Form</h3>
<div *ngIf="invalidCredentialMsg" ngClass="error">{{invalidCredentialMsg}}</div>
<div>
	<form [formGroup]="loginForm" (ngSubmit)="onFormSubmit()">
	  <p>User Name: <input formControlName="username"></p>
	  <p>Password: <input type="password" formControlName="password"></p>
	  <p><button type="submit">Submit</button></p> 
	</form>
</div> 
login.component.ts
import { Component } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
import { Router } from '@angular/router';
import { AuthService } from './services/auth.service';

@Component({
	templateUrl: './login.component.html',
	styleUrls: ['./login.component.css']
})
export class LoginComponent {
	invalidCredentialMsg = '';
	constructor(private authService: AuthService, private router: Router) {
	}
	loginForm = new FormGroup({
		username: new FormControl(),
		password: new FormControl()
	});
	onFormSubmit() {
		let uname = this.loginForm.get('username')?.value;
		let pwd = this.loginForm.get('password')?.value;
		this.authService.isUserAuthenticated(uname, pwd).subscribe(
			(authenticated: boolean) => {
				if (authenticated) {
					let url = this.authService.getRedirectUrl();
					console.log('Redirect Url:' + url);
					this.router.navigate([url]);
				} else {
					this.invalidCredentialMsg = 'Invalid Credentials. Try again.';
				}
			}
		);
	}
} 
logout.component.ts
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { AuthService } from './services/auth.service';
import { User } from './services/user';

@Component({
  selector: 'logout',
  template: `
      <div *ngIf="loggedInUser">
        Logged In: {{loggedInUser.username}} | {{loggedInUser.role}} | 
        <button input='input' (click)="logout()">Logout</button>
      </div>
	`
})
export class LogoutComponent {
  loggedInUser: User | null;
  constructor(private authService: AuthService, private router: Router) {
    this.loggedInUser = this.authService.getLoggedInUser();
  }
  ngOnInit() {
  }
  logout() {
    this.authService.logoutUser();
    this.loggedInUser = null;
    this.router.navigate(["/welcome"]);
  }
} 

2. Code for Admin Feature

admin.component.ts
import { Component } from '@angular/core';
@Component({
	template: `
	  <h2>Welcome to Admin Home</h2>
	  <a [routerLink]="['person-list']" routerLinkActive="active">View Person List</a>
	  <router-outlet></router-outlet>	
  `
})
export class AdminComponent { 
} 
admin-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AdminComponent }  from './admin.component';
import { PersonListComponent }  from './person-list/person.list.component';
import { AuthGuardService }  from '../auth-guard.service';

const personRoutes: Routes = [
	{ 
	  path: '',
	  component: AdminComponent,
	  children: [ 
	    {
	      path: 'person-list',
	      component: PersonListComponent,
	      canActivate: [ AuthGuardService ]
	    }
	  ]
	}  
];

@NgModule({
  imports: [ RouterModule.forChild(personRoutes) ],
  exports: [ RouterModule ]
})
export class AdminRoutingModule { } 
admin.module.ts
import { NgModule }   from '@angular/core';
import { CommonModule }   from '@angular/common';
import { AdminComponent }  from './admin.component';
import { PersonListComponent }  from './person-list/person.list.component';
import { PersonService } from './services/person.service';
import { AdminRoutingModule }  from './admin-routing.module';

@NgModule({
  imports: [     
      CommonModule,
      AdminRoutingModule
  ], 
  declarations: [
      AdminComponent,
      PersonListComponent
  ],
  providers: [ PersonService ]
})
export class AdminModule { 
  constructor() {
    console.log('AdminModule loaded.');
  }
} 
person.ts
export class Person { 
	constructor(public personId:number, public name:string, public city:string) {
	}
} 
person.list.component.html
<h3>Person List</h3>
<p *ngFor="let person of persons$ | async">
   {{person.personId}}. {{person.name}} - {{person.city}}
</p> 
person.list.component.ts
import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { PersonService } from '../services/person.service';
import { Person } from '../person';

@Component({
  templateUrl: './person.list.component.html'
})
export class PersonListComponent implements OnInit {
  persons$: Observable<Person[]>;
  constructor(private personService: PersonService) {
    this.persons$ = this.personService.getPersons();
  }
  ngOnInit() {
  }
} 
person.service.ts
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import { Person } from '../person';

const PERSONS = [
  new Person(1, 'Mahesh', 'Varanasi'),
  new Person(2, 'Ram', 'Ayodhya'),  
  new Person(3, 'Kishna', 'Mathura')
];
let personList$ = of(PERSONS);

@Injectable()
export class PersonService { 
	getPersons(): Observable<Person[]> {
	    return personList$;
	}
} 

3. Code for Address Feature

address.component.ts
import { Component } from '@angular/core';
@Component({
  template: `
      <h3>ADDRESS</h3>
      <p><b> Article: Angular CanLoad Guard Example </b></p>
      <p><b> Category: Angular </b></p>
      <p><b> Website: CONCRETEPAGE.COM </b></p>
      <div>
         <a [routerLink]="['/location']">Find Location</a>
      </div> 
  `
})
export class AddressComponent { 
} 
address-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AddressComponent }  from './address.component';

const addressRoutes: Routes = [
	{ 
	  path: '',
          component: AddressComponent
	}  
];

@NgModule({
  imports: [ RouterModule.forChild(addressRoutes) ],
  exports: [ RouterModule ]
})
export class AddressRoutingModule { } 
address.module.ts
import { NgModule }   from '@angular/core';
import { CommonModule }   from '@angular/common';
import { AddressRoutingModule }   from './address-routing.module';
import { AddressComponent }  from './address.component';

@NgModule({
  imports: [     
    CommonModule,
    AddressRoutingModule
  ], 
  declarations: [
    AddressComponent 
  ]
})
export class AddressModule { 
  constructor() {
    console.log('AddressModule loaded.');
  }
} 

4. Other components, services and modules

auth-guard.service.ts
import { Injectable } from '@angular/core';
import {
  CanLoad, CanActivate, Route, Router,
  ActivatedRouteSnapshot, RouterStateSnapshot
} from '@angular/router';
import { AuthService } from './authentication/services/auth.service';

@Injectable()
export class AuthGuardService implements CanLoad, CanActivate {
  constructor(private authService: AuthService, private router: Router) {
  }
  canLoad(route: Route): boolean {
    const url = route.path;
    console.log('Url:' + url);
    if (this.authService.isUserLoggedIn()) {
      return true;
    }
    this.authService.setRedirectUrl(url);
    this.router.navigate([this.authService.getLoginUrl()]);
    return false;
  }
  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
    const url: string = state.url;
    console.log('Url:' + url);
    if (this.authService.isUserLoggedIn()) {
      return true;
    }
    this.authService.setRedirectUrl(url);
    this.router.navigate([this.authService.getLoginUrl()]);
    return false;
  }
} 
dashboard.layout.component.ts
import { Component } from '@angular/core';
@Component({
  template: `
	  <nav [ngClass] = "'parent-menu'">
	    <ul>
		<li><a routerLink="/address" routerLinkActive="active">Address</a></li>
		<li><a routerLink="/admin" routerLinkActive="active">Admin</a></li>
	    </ul> 
          </nav>  
	  <logout></logout>
	  <div [ngClass] = "'parent-container'">	
	     <router-outlet></router-outlet>	
	  </div>
  `
})
export class DashboardLayoutComponent { 
} 
welcome.component.ts
import { Component } from '@angular/core';
@Component({
  template: `
	    <h2>Welcome to Dashboard</h2>
  `
})
export class WelcomeComponent { 
} 
page-not-found.component.ts
import { Component } from '@angular/core';
import { Location } from '@angular/common';

@Component({
  template: `
        <h2>Page Not Found.</h2>
        <div>
            <button (click)="goBack()">Go Back</button>
	</div>
    `
})
export class PageNotFoundComponent {
    constructor(private location: Location) { }
    goBack(): void {
        this.location.back();
    }
} 
styles.css
.parent-menu ul {
    list-style-type: none;
    margin: 0;
    padding: 0;
    overflow: hidden;
    background-color: #333;
}
.parent-menu li {
    float: left;
}
.parent-menu li a {
    display: block;
    color: white;
    text-align: center;
    padding: 15px 15px;
    text-decoration: none;
}
.parent-menu li a:hover:not(.active) {
    background-color: #111;
}
.parent-menu .active{
    background-color: #4CAF50;
}
.parent-container {
    padding-left: 10px;
} 
app.component.ts
import { Component } from '@angular/core';
@Component({
  selector: 'app-root',
  template: `
	    <router-outlet></router-outlet>	
  `
})
export class AppComponent { 
} 
app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { PreloadAllModules } from '@angular/router';
import { WelcomeComponent } from './welcome.component';
import { DashboardLayoutComponent } from './dashboard.layout.component';
import { PageNotFoundComponent } from './page-not-found.component';
import { AuthGuardService } from './auth-guard.service';

const routes: Routes = [
  {
    path: '',
    redirectTo: '/welcome',
    pathMatch: 'full'
  },
  {
    path: '',
    component: DashboardLayoutComponent,
    children: [
      {
        path: 'address',
        loadChildren: () => import('./address/address.module').then(mod => mod.AddressModule)
      },
      {
        path: 'admin',
        loadChildren: () => import('./admin/admin.module').then(mod => mod.AdminModule),
        canLoad: [AuthGuardService]
      },
      {
        path: 'welcome',
        component: WelcomeComponent
      },
      {
        path: '**',
        component: PageNotFoundComponent
      }
    ]
  }
];

@NgModule({
  imports: [
    RouterModule.forRoot(routes,
      {
        preloadingStrategy: PreloadAllModules
      })
  ],
  exports: [
    RouterModule
  ],
  providers: [AuthGuardService]
})
export class AppRoutingModule { } 
app.module.ts
import { NgModule }   from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent }  from './app.component';
import { WelcomeComponent }  from './welcome.component';
import { LogoutComponent }  from './authentication/logout.component';
import { DashboardLayoutComponent }  from './dashboard.layout.component';
import { PageNotFoundComponent }  from './page-not-found.component';
import { AppRoutingModule }  from './app-routing.module';
import { AuthModule }  from './authentication/auth.module';

@NgModule({
  imports: [     
    BrowserModule,
    AuthModule,
    AppRoutingModule
  ],
  declarations: [
    AppComponent,
    WelcomeComponent,
    LogoutComponent,
    DashboardLayoutComponent,
    PageNotFoundComponent
  ],
  providers: [ ],
  bootstrap: [ AppComponent ]
})
export class AppModule { 
  constructor() {
     console.log('AppModule loaded.');
  }
} 

Run Application

To run the application, find the steps.
1. Download source code using download link given below on this page.
2. Use downloaded src in your Angular CLI application. To install Angular CLI, find the link.
3. Run ng serve using command prompt.
4. Access the URL http://localhost:4200
We will get dashboard. Now access admin feature, we will be redirected to login page. Enter mahesh/m123 authentication and then we will be able to visit admin feature. Find the print screen of the output.
Angular CanLoad Guard Example

References

Common Routing Tasks
CanLoad Interface
Angular Route Guards: CanActivate and CanActivateChild Example

Download Source Code

POSTED BY
ARVIND RAI
ARVIND RAI
LEARN MORE








©2024 concretepage.com | Privacy Policy | Contact Us