Aller au contenu principal

Guards & Resolvers

Route Guards

Protect routes from unauthorized access.

CanActivate

import { inject } from '@angular/core';
import { Router, CanActivateFn } 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;
}

// Rediriger vers login
return router.createUrlTree(['/login']);
};

CanActivateChild

export const adminGuard: CanActivateFn = (route, state) => {
const authService = inject(AuthService);
return authService.hasRole('admin');
};

CanDeactivate

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

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

CanMatch

export const featureGuard: CanMatchFn = (route, segments) => {
const featureService = inject(FeatureService);
return featureService.isFeatureEnabled('newFeature');
};

Resolvers

Pré-charger les données avant d'activer la route.

Resolver de Base

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(id);
};

Resolvers Multiples

export const userResolver: ResolveFn<User> = (route) => {
return inject(UserService).getUser(route.params['id']);
};

export const postsResolver: ResolveFn<Post[]> = (route) => {
return inject(PostService).getPosts(route.params['id']);
};

Gestion des Erreurs dans Resolver

import { catchError, EMPTY } from 'rxjs';

export const userResolver: ResolveFn<User> = (route) => {
const userService = inject(UserService);
const router = inject(Router);

return userService.getUser(route.params['id']).pipe(
catchError(error => {
console.error('Failed to load user', error);
router.navigate(['/not-found']);
return EMPTY; // Ne pas naviguer vers la route
})
);
};

Combiner Guards et Resolvers

export const authGuard: CanActivateFn = (route, state) => {
return inject(AuthService).isLoggedIn();
};

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

Guard Basé sur les Rôles

export function roleGuard(roles: string[]): CanActivateFn {
return (route, state) => {
const authService = inject(AuthService);
const router = inject(Router);

const userRoles = authService.getUserRoles();
const hasRole = roles.some(role => userRoles.includes(role));

if (hasRole) {
return true;
}

return router.createUrlTree(['/unauthorized']);
};
}

Guard Asynchrone

export const permissionGuard: CanActivateFn = (route, state) => {
const permissionService = inject(PermissionService);
const router = inject(Router);

return permissionService.checkPermission(route.data['permission']).pipe(
map(hasPermission => {
if (hasPermission) {
return true;
}
return router.createUrlTree(['/forbidden']);
})
);
};

Guard avec Redirection

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

if (authService.isLoggedIn()) {
return true;
}

// Rediriger vers login avec l'URL de retour
return router.createUrlTree(['/login'], {
queryParams: { returnUrl: state.url }
});
};

Bonnes Pratiques

  • Utiliser les guards pour l'authentification/autorisation
  • Utiliser les resolvers pour pré-charger les données critiques
  • Toujours gérer les erreurs dans les resolvers
  • Retourner UrlTree au lieu de router.navigate() dans les guards
  • Garder les guards simples et ciblés
  • Utiliser canMatch pour les feature flags
  • Utiliser canDeactivate pour les changements non sauvegardés
  • Combiner plusieurs guards si nécessaire
  • Considérer les états de chargement pour les resolvers