Skip to content
intermediate Phase 8 · Routing and Navigation Guards

Guards and Resolvers

Block, redirect, or prefetch before a route activates.

45m
0 problems
Topic Progress 0%

Functional Route Guards

Functional guards are the modern approach. They are simple functions that decide whether navigation proceeds.

canActivate — Protect a Route

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

export const authGuard: CanActivateFn = (route, state) => {
  const authService = inject(AuthService);
  const router = inject(Router);

  if (authService.isLoggedIn) {
    return true;
  }

  // Redirect to login with return URL
  return router.createUrlTree(['/login'], {
    queryParams: { returnUrl: state.url },
  });
};
export const routes: Routes = [
  { path: 'dashboard', component: DashboardComponent, canActivate: [authGuard] },
];

canMatch — Conditionally Match a Route

export const adminGuard: CanMatchFn = (route, segments) => {
  const authService = inject(AuthService);
  return authService.isAdmin;
};

export const routes: Routes = [
  {
    path: 'admin',
    canMatch: [adminGuard],
    loadChildren: () => import('./admin/admin.routes'),
  },
];

canDeactivate — Prevent Leaving a Route

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

export const unsavedChangesGuard: CanDeactivateFn<CanComponentDeactivate> = (
  component: CanComponentDeactivate
) => {
  return component.canDeactivate ? component.canDeactivate() : true;
};

Guard Return Types

Return Effect
true Allow navigation
false Cancel navigation
UrlTree Redirect to another route
Observable<boolean | UrlTree> Async decision
Promise<boolean | UrlTree> Async decision

Resolvers — Prefetching Data

Resolvers fetch data before a route activates, ensuring the component renders with content already available.

Creating a Resolver

import { inject } from '@angular/core';
import { ResolveFn } from '@angular/router';
import { UserService } from './user.service';

export const userResolver: ResolveFn<User> = (route, state) => {
  const userService = inject(UserService);
  const id = route.paramMap.get('id')!;
  return userService.getUser(Number(id));
};
export const routes: Routes = [
  {
    path: 'users/:id',
    component: UserDetailComponent,
    resolve: { user: userResolver },
  },
];

Accessing Resolved Data

@Component({
  selector: 'app-user-detail',
  template: `<h1>{{ user.name }}</h1>`,
})
export class UserDetailComponent {
  private route = inject(ActivatedRoute);
  user!: User;

  constructor() {
    this.user = this.route.snapshot.data['user'];
  }
}

Resolver with Loading State

export const userResolver: ResolveFn<User> = (route, state) => {
  const userService = inject(UserService);
  const id = route.paramMap.get('id')!;

  return userService.getUser(Number(id)).pipe(
    catchError(() => {
      // Handle error — redirect or return default
      return of(null);
    })
  );
};

Guard + Resolver Combination

export const routes: Routes = [
  {
    path: 'users/:id',
    canActivate: [authGuard],
    resolve: { user: userResolver },
    component: UserDetailComponent,
  },
];

The guard runs first. If it passes, the resolver fetches data. Then the component renders with the resolved data.

Quiz

1. What is the modern functional guard syntax?

Question 1 options

2. How do you redirect in a guard?

Question 2 options

3. What does a resolver do?

Question 3 options

Flashcards

Question

What is a functional guard?

Answer

CanActivateFn — a function using inject() that returns true, false, or UrlTree.

Question

How do you redirect in a guard?

Answer

return router.createUrlTree(['/login']) — returning a UrlTree redirects.

Question

What does a resolver do?

Answer

Fetches data before route activation. Available via route.snapshot.data.

Revision Notes

Key Takeaways

  • 1. Functional guards (CanActivateFn) are the modern approach
  • 2. Return UrlTree to redirect from a guard
  • 3. Resolvers prefetch data before route activation
  • 4. Guards run before resolvers

Interview Tips

  • • Explain functional vs class-based guards
  • • Be ready to describe the guard return types
  • • Mention resolvers for data prefetching

Cheat Sheet

Guards and Resolvers Cheat Sheet

Functional guard:

export const authGuard: CanActivateFn = (route, state) => {
  return inject(AuthService).isLoggedIn
    ? true
    : inject(Router).createUrlTree(['/login']);
};

Resolver:

export const userResolver: ResolveFn<User> = (route) => {
  return inject(UserService).getUser(route.paramMap.get('id')!);
};

Route config:

{ path: 'users/:id', canActivate: [authGuard], resolve: { user: userResolver } }

Continue Learning