Angular Route Guards And Resolver Services Complete Guide

 Last Update:2025-06-22T00:00:00     .NET School AI Teacher - SELECT ANY TEXT TO EXPLANATION.    8 mins read      Difficulty-Level: beginner

Understanding the Core Concepts of Angular Route Guards and Resolver Services

Explaining Angular Route Guards and Resolver Services - Important Info

Route Guards

Route guards are interfaces provided by the Angular Router that can be implemented to define custom logic for navigating to or from a particular route. They enable you to add conditions that must be met for a route to be activated or deactivated. Here are the important route guard interfaces:

  1. CanActivate: This interface allows us to check authentication or other preconditions before the route is loaded.

    canActivate(
      next: ActivatedRouteSnapshot,
      state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
      // Return true/false or an Observable/Promise that resolves to true/false
    }
    
  2. CanActivateChild: Similar to CanActivate, but this one decides whether the child routes of the parent route should be activated.

    canActivateChild(
      childRoute: ActivatedRouteSnapshot,
      state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
      // Return true/false or an Observable/Promise that resolves to true/false
    }
    
  3. CanDeactivate: Use this interface when you want to implement logic that decides if the currently active route (and its component) can be navigated away. A common use case for this is prompting users about unsaved changes.

    canDeactivate(
      component: any,
      currentRoute: ActivatedRouteSnapshot,
      currentState: RouterStateSnapshot,
      nextState?: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
      // Return true/false or an Observable/Promise that resolves to true/false
    }
    
  4. Resolve: The Resolve interface is similar to CanActivate, but it provides a way to fetch data before a component is instantiated. This is especially useful when you need to ensure the data is fetched before the route is rendered, avoiding loading issues.

    resolve(
      route: ActivatedRouteSnapshot,
      state: RouterStateSnapshot): Observable<any> | Promise<any> | any {
      // Fetch and return data as an observable, promise, or regular object
    }
    
  5. CanLoad: The CanLoad guard protects lazy-loaded modules and their routes, ensuring that the module's code is only loaded and activated when certain conditions are met. It’s often used with lazy loading to prevent unauthorized access to parts of your application.

    canLoad(
      route: Route,
      segments: UrlSegment[]): Observable<boolean> | Promise<boolean> | boolean {
      // Return true/false or an Observable/Promise that resolves to true/false
    }
    

Resolver Services

Resolver services are used in conjunction with route guards to fetch data asynchronously before a route is activated. This ensures that the required data is fetched before the component is instantiated, making the route transition smoother and more robust.

Here's how you might set up a resolver service:

@Injectable()
export class DataResolver implements Resolve<any> {

  constructor(private apiService: ApiService) {}

  resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> {
    const id = route.paramMap.get('id');
    return this.apiService.fetchData(id).pipe(
      catchError(error => {
        console.error('Error fetching data', error);
        return of(null);
      })
    );
  }
}

In this example, when the route activates, it will call the resolve method, which fetches data based on a parameter (id) passed via the route configuration.

Configuring Routes with Guards and Resolvers

Once you have defined your guards and resolvers, you can configure them in your route definitions. Here’s how:

const appRoutes: Routes = [
  { path: 'dashboard', component: DashboardComponent, canActivate: [AuthGuard], resolve: { data: DataResolver } },
  { path: 'products/:id', component: ProductDetailComponent, canActivate: [AuthGuard], resolve: { productDetails: ProductDetailResolver } },
  { path: 'lazy-module', loadChildren: () => import('./lazy.module').then(m => m.LazyModule), canLoad: [FeatureGuard] }
];

@NgModule({
  imports: [RouterModule.forRoot(appRoutes)],
  exports: [RouterModule],
  providers: [AuthGuard, FeatureGuard, DataResolver, ProductDetailResolver]
})
export class AppRoutingModule {}

Here, DashboardComponent and ProductDetailComponent require authentication and also have resolvers for fetching data (data and productDetails respectively). The lazy-module requires feature-specific permissions before being loaded.

Benefits of Using Route Guards and Resolvers

  • Conditional Navigation: Route guards ensure that components are only instantiated if certain conditions are met. For example, a user can only navigate to a specific route if they are logged in.
  • Pre-fetching Data: Resolvers load necessary data asynchronously ahead of time, making sure that the component receives all the needed data before activation, leading to a smoother user experience.
  • Code Organization: By separating concerns into guards and resolvers, your code becomes more organized, maintainable, and modular.
  • Performance Optimization: Pre-fetching data with resolvers helps to eliminate unnecessary API calls once the route is activated, potentially improving the performance of your application.

Understanding and utilizing Angular Route Guards and Resolver Services effectively can significantly enhance the security and user experience of your applications, making it crucial for every Angular developer to grasp their functionalities and use cases.


Online Code run

🔔 Note: Select your programming language to check or run code at

💻 Run Code Compiler

Step-by-Step Guide: How to Implement Angular Route Guards and Resolver Services

Step-by-Step Example: Angular Route Guards

Prerequisites

Ensure you have Angular CLI installed. If not, install it using:

npm install -g @angular/cli

1. Create a New Angular Project

Let's start by creating a new Angular project:

ng new route-guards-example
cd route-guards-example

2. Generate Some Components

Generate a couple of components for our application:

ng generate component login
ng generate component dashboard

3. Configure Routes

In the app-routing.module.ts file, set up routes to the LoginComponent and DashboardComponent. Define a sample route guard:

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { LoginComponent } from './login/login.component';
import { DashboardComponent } from './dashboard/dashboard.component';
import { AuthGuard } from './auth.guard'; // This will be created in the next step

const routes: Routes = [
  { path: 'login', component: LoginComponent },
  { path: 'dashboard', component: DashboardComponent, canActivate: [AuthGuard] },
  { path: '', redirectTo: '/login', pathMatch: 'full' }
];

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

4. Create the AuthGuard

Create a new file for the AuthGuard. This service will intercept requests to the DashboardComponent and ensure the user is authenticated:

ng generate guard auth

By default, Angular CLI generates a service named auth.guard.ts. Here’s an example implementation:

import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router';
import { Observable } from 'rxjs';

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

  constructor(private router: Router) {}

  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
    // Assume user is logged in based on some condition
    let isLoggedIn = sessionStorage.getItem('isLoggedIn') === 'true';

    if (isLoggedIn) {
      return true;
    } else {
      this.router.navigate(['/login']);
      return false;
    }
  }
}

5. Update LoginComponent for Testing

Update the LoginComponent to simulate logging in:

import { Component } from '@angular/core';
import { Router } from '@angular/router';

@Component({
  selector: 'app-login',
  template: `
    <div>
      <h1>Login</h1>
      <button (click)="login()">Login</button>
    </div>
  `
})
export class LoginComponent {

  constructor(private router: Router) {}

  login() {
    sessionStorage.setItem('isLoggedIn', 'true');
    this.router.navigate(['/dashboard']);
  }
}

6. Test the Route Guard

Run the application using ng serve and navigate to /dashboard. Since you are not logged in initially, you should be redirected to the /login page. After clicking the "Login" button, the isLoggedIn flag should be set, allowing you to navigate to /dashboard.

Step-by-Step Example: Angular Resolver Services

Prerequisites

Ensure you have Angular CLI installed. If not, install it using:

npm install -g @angular/cli

1. Create a New Angular Project

Let’s set up a new project for the Resolver Service example:

ng new resolver-example
cd resolver-example

2. Generate Some Components and Services

Generate a couple of components and a service for our application:

ng generate component profile
ng generate service user

3. Configure Routes

In the app-routing.module.ts file, set up routes to the ProfileComponent. Define a resolver service:

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { ProfileComponent } from './profile/profile.component';
import { UserResolver } from './user.resolver'; // This will be created in the next step

const routes: Routes = [
  { path: 'profile/:id', component: ProfileComponent, resolve: { user: UserResolver } },
  { path: '', redirectTo: '/profile/1', pathMatch: 'full' }
];

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

4. Create the UserResolver

Create a new file for the UserResolver. This service will fetch data for the user profile before the ProfileComponent is activated:

ng generate resolver user

By default, Angular CLI generates a service named user.resolver.ts. Here’s an example implementation:

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

@Injectable({
  providedIn: 'root'
})
export class UserResolver implements Resolve<any> {
  constructor(private userService: UserService) {}

  resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> {
    const userId = route.paramMap.get('id');
    return this.userService.getUserById(Number(userId));
  }
}

5. Implement the UserService

Next, implement a UserService to fetch the user data. For simplicity, we will return a mock user object:

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

@Injectable({
  providedIn: 'root'
})
export class UserService {

  constructor() {}

  getUserById(id: number): Observable<any> {
    const mockUser = {
      id: 1,
      name: 'John Doe',
      email: 'john.doe@example.com'
    };

    return of(mockUser);
  }
}

6. Update ProfileComponent to Use Resolved Data

Finally, update the ProfileComponent to consume the resolved user data:

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

@Component({
  selector: 'app-profile',
  template: `
    <div>
      <h1>User Profile</h1>
      <p>ID: {{ user?.id }}</p>
      <p>Name: {{ user?.name }}</p>
      <p>Email: {{ user?.email }}</p>
    </div>
  `,
  styles: []
})
export class ProfileComponent implements OnInit {
  user: any;

  constructor(private route: ActivatedRoute) {}

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

7. Test the Resolver

Run the application using ng serve and navigate to /profile/1. The resolver should fetch the user data and display it in the ProfileComponent.

Top 10 Interview Questions & Answers on Angular Route Guards and Resolver Services

1. What are Angular Route Guards?

Answer: Angular Route Guards are interfaces that provide methods to enable or disable routing based on specific criteria at runtime. They help in controlling access to routes, redirecting users, or pre-fetching data before navigation.

2. How many types of Route Guards exist in Angular?

Answer: There are five types of route guards in Angular:

  • CanActivate: Decides whether a route can be activated.
  • CanActivateChild: Determines if a child route of a feature module should be activated.
  • CanDeactivate: Allows you control whether a user can leave a certain route by deciding which component is being deactivated.
  • Resolve: Pre-fetches data before the route is officially activated.
  • CanLoad: Helps prevent the asynchronous loading of a module until a certain condition is met, ensuring lazy-loaded modules behave correctly.

3. What is the primary purpose of a CanActivate guard?

Answer: The primary purpose of a CanActivate guard is to decide whether a user can navigate to a particular route. It checks conditions such as authentication status and returns a boolean value to either allow or deny route activation.

4. How do you create a Resolver service in Angular?

Answer: To create a Resolver service in Angular:

  1. Generate the resolver: ng generate resolver yourResolverName.
  2. Implement the Resolve interface in the generated service.
  3. Define the data fetching logic within the resolve() method.
  4. Register the resolver in your module's routing configuration using the resolve property.

Example:

@Injectable({ providedIn: 'root' })
export class UserDataResolver implements Resolve<User> {
  constructor(private userService: UserService) {}

  resolve(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): Observable<User> {
    const userId = route.paramMap.get('id');
    return this.userService.getUserById(userId);
  }
}

Then use it within the routes:

const routes: Routes = [
  { 
    path: 'user/:id', 
    component: UserComponent, 
    resolve: { userData: UserDataResolver } 
  },
];

5. Why should you use Resolvers with lazy loading?

Answer: Using Resolvers with lazy loading ensures that data required by the component is fetched before the module is loaded and its components are instantiated. This enhances user experience by pre-fetching necessary data, making the transition smoother.

6. What are some scenarios where you would use a CanDeactivate guard?

Answer: CanDeactivate guards are often used for form validation or warning users when they discard unsaved changes. For example, prompting users before they navigate away from a page with an unsaved form would prevent accidental data loss.

7. Explain the difference between CanActivate and CanActivateChild guards.

Answer:

  • CanActivate: Controls whether a route can be activated. If a user navigates directly to a route or uses the router to navigate to a route, CanActivate will ensure that the user meets any specified conditions.
  • CanActivateChild: Specifically controls access to child routes within a parent route. When a user tries to access a child route, CanActivateChild will check the conditions for that child route while CanActivate checks conditions for the parent route.

8. Can you use multiple Route Guards on a single route?

Answer: Yes, you can use multiple Route Guards on a single route by providing them as an array in the canActivate, canActivateChild, canDeactivate, canLoad, and resolve properties.

Example:

const routes: Routes = [
  { 
    path: 'profile', 
    component: ProfileComponent, 
    canActivate: [AuthGuard, RoleGuard] 
  },
];

9. What is the recommended way to inject services into route guards?

Answer: The recommended way to inject services into route guards is to use Angular's Dependency Injection system. You simply add the service to the constructor of your guard, declaring the dependency like you do for any other service.

Example:

@Injectable({ providedIn: 'root' })
export class AuthGuard implements CanActivate {
  constructor(private authService: AuthService, private router: Router) {}
  
  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): boolean | Observable<boolean> | Promise<boolean> {
    if (this.authService.isAuthenticated()) {
      return true;
    } else {
      this.router.navigate(['/login']);
      return false;
    }
  }
}

10. Should you use Resolvers instead of services inside components?

Answer: While resolvers are excellent for loading data needed before the component is rendered, their usage is not mutually exclusive with services in components. Use resolvers to load critical route-specific data upfront, but continue to implement services within components for handling dynamic data loading, operations related to user interactions, and ongoing data updating requirements.

You May Like This Related .NET Topic

Login to post a comment.