Guide to Angular Guards

Introduction

Angular Guards are a powerful feature that allows you to control access to routes in an Angular application. They help in securing the application by determining if a user can access a particular route or not. This article will provide a detailed understanding of Angular Guards, including their types, implementation, and practical examples.

Types of Angular Guards

Angular provides five types of guards.

  1. CanActivate: Determines if a route can be activated.
  2. CanActivateChild: Determines if a child route can be activated.
  3. CanDeactivate: Determines if a route can be deactivated.
  4. Resolve Pre-fetches data before a route is activated.
  5. CanLoad: Determines if a module can be loaded lazily.

1. CanActivate

The CanActivate guard is used to decide if a route can be activated. It is commonly used for authentication and authorization purposes.

import { Injectable } from '@angular/core';
import { CanActivate, Router } from '@angular/router';
import { AuthService } from './auth.service';
@Injectable({
  providedIn: 'root'
})
export class AuthGuard implements CanActivate {

  constructor(
    private authService: AuthService,
    private router: Router
  ) {}

  canActivate(): boolean {
    if (this.authService.isLoggedIn()) {
      return true;
    } else {
      this.router.navigate(['login']);
      return false;
    }
  }
}

2. CanActivateChild

The CanActivateChild guard works similarly to CanActivate but is applied to child routes.

import { Injectable } from '@angular/core';
import { CanActivateChild, Router } from '@angular/router';
import { AuthService } from './auth.service';

@Injectable({
  providedIn: 'root'
})
export class ChildAuthGuard implements CanActivateChild {

  constructor(private authService: AuthService, private router: Router) {}

  canActivateChild(): boolean {
    if (this.authService.isLoggedIn()) {
      return true;
    } else {
      this.router.navigate(['login']);
      return false;
    }
  }
}

3. CanDeactivate

The CanDeactivate guard is used to decide if a route can be exited. It is useful for prompting the user to save changes before navigating away from a form.

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 CanDeactivateGuard implements CanDeactivate<CanComponentDeactivate> {
  canDeactivate(component: CanComponentDeactivate): Observable<boolean> | Promise<boolean> | boolean {
    return component.canDeactivate ? component.canDeactivate() : true;
  }
}

4. Resolve

The Resolve guard pre-fetches data before a route is activated, ensuring that the required data is available when the route is activated.

import { Injectable } from '@angular/core';
import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs';
import { DataService } from './data.service';

@Injectable({
  providedIn: 'root'
})
export class DataResolver implements Resolve<any> {

  constructor(private dataService: DataService) {}

  resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> {
    return this.dataService.getData();
  }
}

5. CanLoad

The CanLoad guard is used to determine if a module can be loaded lazily. It prevents the loading of modules if certain conditions are not met.

import { Injectable } from '@angular/core';
import { CanLoad, Route, UrlSegment, Router } from '@angular/router';
import { AuthService } from './auth.service';
@Injectable({
  providedIn: 'root'
})
export class CanLoadGuard implements CanLoad {

  constructor(
    private authService: AuthService,
    private router: Router
  ) {}

  canLoad(route: Route, segments: UrlSegment[]): boolean {
    if (this.authService.isLoggedIn()) {
      return true;
    } else {
      this.router.navigate(['login']);
      return false;
    }
  }
}

Implementing Guards in Routes

To use guards in your routes, you need to add them to the route configuration.

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HomeComponent } from './home/home.component';
import { LoginComponent } from './login/login.component';
import { AuthGuard } from './auth.guard';
import { ChildAuthGuard } from './child-auth.guard';
import { CanDeactivateGuard } from './can-deactivate.guard';
import { DataResolver } from './data.resolver';
import { CanLoadGuard } from './can-load.guard';
import { AdminComponent } from './admin/admin.component';  // Assuming you have this component
import { SettingsComponent } from './settings/settings.component';  // Assuming you have this component
import { FormComponent } from './form/form.component';  // Assuming you have this component

const routes: Routes = [
  {
    path: 'home',
    component: HomeComponent,
    canActivate: [AuthGuard],
    resolve: { data: DataResolver }
  },
  { path: 'login', component: LoginComponent },
  {
    path: 'admin',
    component: AdminComponent,
    canActivateChild: [ChildAuthGuard],
    children: [
      { path: 'settings', component: SettingsComponent }
    ]
  },
  {
    path: 'form',
    component: FormComponent,
    canDeactivate: [CanDeactivateGuard]
  },
  {
    path: 'lazy',
    loadChildren: () => import('./lazy/lazy.module').then(m => m.LazyModule),
    canLoad: [CanLoadGuard]
  }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

Conclusion

Angular Guards are essential for managing access control in your applications. They provide a way to implement security checks and manage user permissions efficiently. By leveraging the different types of guards, you can ensure that your application behaves correctly under various scenarios, such as authentication, authorization, data pre-fetching, and preventing unsaved changes.

Implementing guards effectively can enhance the security and usability of your Angular application, making it robust and user-friendly.

Happy Coding!