DEV Community

Rohit Cdy
Rohit Cdy

Posted on

Mastering Angular Routing & Guards: Secure and Seamless Navigation!

In an SPA, Angular routing is basically the functionality allowing users to travel between various parts of your app. At that point, the route guards, which will make navigation smarter and more secure.

Why Would We need Angular Guard Protection?

  1. Block Access: Let unauthorized users have no access to the routes you select.
  2. Role-Based Access Control: Grant or deny access according to user roles.
  3. Avoid Unsaved Data Loss: Prompt the user for unsaved changes when navigating away from the page.
  4. Preloading of Data Before Navigation: Load data needed before a component is routed in.
  5. Lazy Loading Optimization: Manage lazy loading of modules efficiently.

Types of Route Guards in Angular

There are five different types of route guards in Angular:

  1. canActivate: Checks whether the user can go to a certain route.
  2. canActivateChild: Determines whether users should be able to reach child routes.
  3. canDeactivate: Prevents users from leaving a route unless they meet some condition.
  4. resolve: It pre-loads data ahead of the route's activation.
  5. canLoad: Disallows loading of lazy-loaded modules if the condition is not met.

How to Use Route Guards in Angular

1. canActivate - Block Access

This guard prevents unauthorized access to certain routes.

Implementation:

import { Injectable } from '@angular/core';
import { CanActivate, Router } from '@angular/router';

@Injectable({ providedIn: 'root' })
export class AuthGuard implements CanActivate {
  constructor(private router: Router) {}

  canActivate(): boolean {
    const isAuthenticated =!!localStorage.getItem('token');
    if (!isAuthenticated) {
      this.router.navigate(['/login']);
      return false;
    }
return true;
  }
}
Enter fullscreen mode Exit fullscreen mode

Usage:

{ path: 'dashboard', component: DashboardComponent, canActivate: [AuthGuard] }
Enter fullscreen mode Exit fullscreen mode

2. canActivateChild - Protect Child Routes

This guard restricts access to child routes just like canActivate does for parent routes.

Implementation:

import { Injectable } from '@angular/core';
import { CanActivateChild, Router } from '@angular/router';

@Injectable({ providedIn: 'root' })
export class ChildAuthGuard implements CanActivateChild {
  constructor(private router: Router) {}

  canActivateChild(): boolean {
const hasPermission =!!localStorage.getItem('admin');
    if (!hasPermission) {
      this.router.navigate(['/']);
      return false;
    }
    return true;
  }
}

Enter fullscreen mode Exit fullscreen mode

Usage:

{
  path: 'admin',
  component: AdminComponent,
  canActivateChild: [ChildAuthGuard],
  children: [
    { path: 'users', component: UsersComponent },
    { path: 'settings', component: SettingsComponent }]
}
Enter fullscreen mode Exit fullscreen mode

3. canDeactivate - Prevent Accidental Data Loss

This guard prevents the user from navigating away from a certain component if there are unsaved changes.

Solution:

import { Injectable } from '@angular/core';
import { CanDeactivate } from '@angular/router';
import { Observable } from 'rxjs';

export interface CanComponentDeactivate {
  canDeactivate: () => Observable<boolean> | Promise<boolean> | boolean;
}

@Injectable({ providedIn: 'root' })
export class CanLeaveGuard implements CanDeactivate<CanComponentDeactivate> {
  canDeactivate(component: CanComponentDeactivate): boolean {
return component.canDeactivate? component.canDeactivate() : true;
  }
}
Enter fullscreen mode Exit fullscreen mode

Component Implementation:

import { Component } from '@angular/core';
import { CanComponentDeactivate } from './guards/can-leave.guard';

@Component({
  selector: 'app-profile',
  templateUrl: './profile.component.html'
})
export class ProfileComponent implements CanComponentDeactivate {
  unsavedChanges = true;

  canDeactivate(): boolean {
    return this.unsavedChanges? confirm('You have unsaved changes. Leave anyway?') : true;
  }
}
Enter fullscreen mode Exit fullscreen mode

Usage:

{ path: 'profile', component: ProfileComponent, canDeactivate: [CanLeaveGuard] }
Enter fullscreen mode Exit fullscreen mode

4. resolve - Fetch Data Before Route Activation

This ensures that data are fetched before the route is activated.

Implementation:

import { Injectable } from '@angular/core';
import { Resolve } from '@angular/router';
import { Observable, of } from 'rxjs';

@Injectable({ providedIn: 'root' })
export class UserResolver implements Resolve<any> {
  resolve(): Observable<any> {
    return of({ name: 'John Doe', age: 30 }); // Example API call
  }
}
Enter fullscreen mode Exit fullscreen mode

Usage:

{ path: 'user', component: UserComponent, resolve: { userData: UserResolver } }
Enter fullscreen mode Exit fullscreen mode

How to access resolved data in Component:

import { ActivatedRoute } from '@angular/router';

constructor(private route: ActivatedRoute) {}

ngOnInit() {
  const user = this.route.snapshot.data['userData'];
  console.log(user);
}
Enter fullscreen mode Exit fullscreen mode

5. canLoad - Blocking Lazy Module Loading

This guard prevents lazy-loaded modules to be loaded in case conditions aren't met.

Implementation:

import { Injectable } from '@angular/core';
import { CanLoad, Route, UrlSegment, Router } from '@angular/router';

@Injectable({ providedIn: 'root' })
export class AdminLoadGuard implements CanLoad {
  constructor(private router: Router) {}

  canLoad(route: Route, segments: UrlSegment[]): boolean {
    const isAdmin =!!localStorage.getItem('admin');
    if (!isAdmin) {
      this.router.navigate(['/']);
      return false;
    }
    return true;
  }
}
Enter fullscreen mode Exit fullscreen mode

Usage:

{ path: 'admin', loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule), canLoad: [AdminLoadGuard] }
Enter fullscreen mode Exit fullscreen mode

Conclusion

Route guards in Angular play a great role in route protection, navigation control, and optimization of performance. You will efficiently manage your users' access and data flow within your applications by implementing the different guards of canActivate, canActivateChild, canDeactivate, resolve, and canLoad.

Top comments (0)