Angular Routing Guards

 

In Angular, there are three main types of routing guards that can be used to control the navigation behavior: CanActivate, CanDeactivate, and Resolve.

CanActivate

This guard is used to control whether the user is allowed to navigate to a particular route or not. It returns a boolean or an Observable<boolean> that indicates whether the user is allowed to navigate. Here's an example of how to use CanActivate:

import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree, Router } from '@angular/router';
import { Observable } from 'rxjs';
import { AuthService } from './auth.service';

@Injectable({
  providedIn: 'root'
})
export class AuthGuard implements CanActivate {

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

  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
    if (this.authService.isLoggedIn()) {
      return true;
    } else {
      this.router.navigate(['/login']);
      return false;
    }
  }
}

In this example, AuthGuard is a service that implements the CanActivate interface. The canActivate method checks whether the user is logged in using the AuthService and returns true if the user is logged in. If the user is not logged in, the method redirects to the login page using the Router.

 

CanDeactivate

This guard is used to control whether the user is allowed to navigate away from a particular route or not. It returns a boolean or an Observable<boolean> that indicates whether the user is allowed to navigate away. Here's an example of how to use CanDeactivate:

import { Injectable } from '@angular/core';
import { CanDeactivate } from '@angular/router';
import { Observable } from 'rxjs';
import { CanComponentDeactivate } from './can-deactivate.interface';

@Injectable({
  providedIn: 'root'
})
export class CanDeactivateGuard implements CanDeactivate<CanComponentDeactivate> {
  
  canDeactivate(
    component: CanComponentDeactivate,
    currentRoute: ActivatedRouteSnapshot,
    currentState: RouterStateSnapshot,
    nextState?: RouterStateSnapshot
  ): boolean | Observable<boolean> | Promise<boolean> {
    return component.canDeactivate();
  }

}

In this example, CanDeactivateGuard is a service that implements the CanDeactivate interface. The canDeactivate method checks whether the component implements the CanComponentDeactivate interface, which has a method canDeactivate that returns a boolean or an Observable<boolean> indicating whether the user is allowed to navigate away.

 

Resolve

The Resolve guard is a way to asynchronously retrieve data before the route is activated. This means that you can load data and ensure that it is available to your components before they are displayed. This can be useful for cases where you need to fetch data from a backend API or a database.

To use the Resolve guard, you need to define a class that implements the Resolve interface. The interface requires you to implement a method called resolve, which returns an observable that emits the data you want to retrieve. Here is an example:

import { Injectable } from '@angular/core';
import { Resolve, ActivatedRouteSnapshot } 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): Observable<any> {
    const id = route.paramMap.get('id');
    return this.dataService.getData(id);
  }
}

In this example, we define a DataResolver class that implements the Resolve interface. The constructor takes a DataService object that will be used to retrieve the data. The resolve method takes an ActivatedRouteSnapshot object as a parameter and extracts the id parameter from it. Then, it calls the getData method on the dataService object and returns the observable that it returns.

To use the resolver, you need to add it to the resolve property of a route definition:

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { DataComponent } from './data.component';
import { DataResolver } from './data.resolver';

const routes: Routes = [
  {
    path: 'data/:id',
    component: DataComponent,
    resolve: {
      data: DataResolver
    }
  }
];

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

In this example, we define a route that uses the DataResolver to load the data. The resolve property of the route definition is an object that maps the name of the data to the resolver class. In this case, the name of the data is data and the resolver is the DataResolver class.

In the DataComponent, we can access the resolved data by injecting the ActivatedRoute and subscribing to the data property of the route:

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

@Component({
  selector: 'app-data',
  templateUrl: './data.component.html',
  styleUrls: ['./data.component.css']
})
export class DataComponent implements OnInit {
  data: any;

  constructor(private route: ActivatedRoute) {}

  ngOnInit() {
    this.route.data.subscribe(data => {
      this.data = data.data;
    });
  }
}

In this example, we inject the ActivatedRoute and subscribe to the data property of the route. When the route is activated, the resolved data will be available in the data property of the route, which we can then assign to a property of the component.

That's a brief overview of how to use the Resolve guard in Angular. By using this guard, you can ensure that your components have the data they need before they are displayed, which can make your application more responsive and user-friendly.

 

Conclusion

Angular routing is a powerful feature that allows for efficient navigation between different views and components in a single-page application. Traditional routing can be used to load the entire application when a new URL is navigated to, while lazy loading can be used to load components or modules only when they are needed. By using these features, developers can create efficient and responsive Angular applications that provide a great user experience.