Générez le code TypeScript de vos Guards Angular (CanActivate, CanDeactivate, CanMatch) et Resolvers. Style functional ou class-based, prêt à copier-coller.
Générateur de Guard & Resolver Angular
🔧 Configuration
Guards et Resolvers Angular — Guide complet
Dans Angular, les Guards et Resolvers sont des intercepteurs du cycle de navigation du Router. Ils permettent de contrôler qui peut accéder à quoi, quand et avec quelles données — sans polluer vos composants avec de la logique de navigation. Ce guide couvre les cinq types disponibles, la différence entre les functional guards (Angular 15+) et les class-based guards, ainsi que les bonnes pratiques d'organisation dans vos projets.
CanActivate — Protéger l'accès à une route
CanActivate est le guard le plus courant. Il s'exécute avant l'activation d'un composant et permet de bloquer la navigation si une condition n'est pas remplie — par exemple, l'utilisateur n'est pas authentifié.
// Functional guard (Angular 15+) — auth.guard.ts
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.isAuthenticated()) {
return true;
}
// Redirige vers /login avec l'URL d'origine en query param
return router.createUrlTree(['/login'], {
queryParams: { returnUrl: state.url }
});
};
Le guard peut retourner un boolean, une UrlTree (redirection) ou un Observable<boolean | UrlTree>. En retournant une UrlTree plutôt que false, Angular gère la redirection de façon atomique et évite les conditions de course.
CanActivateChild — Protéger les routes enfants
CanActivateChild fonctionne comme CanActivate mais s'applique à chaque route enfant d'un groupe. Il est idéal pour protéger une section complète (ex: tous les sous-routes d'un espace admin) sans dupliquer le guard sur chaque route.
// app.routes.ts — Protéger toutes les routes enfants de /admin
{
path: 'admin',
canActivateChild: [authGuard],
children: [
{ path: 'users', component: UsersComponent },
{ path: 'settings', component: SettingsComponent },
{ path: 'logs', component: LogsComponent },
]
}
CanDeactivate — Bloquer la sortie d'une route
CanDeactivate s'exécute avant que l'utilisateur quitte une route. C'est l'outil parfait pour avertir d'un formulaire non sauvegardé ou d'une action en cours. Le guard a accès à l'instance du composant actif via son type générique.
// unsaved-changes.guard.ts
import { CanDeactivateFn } from '@angular/router';
import { EditFormComponent } from './edit-form.component';
export const unsavedChangesGuard: CanDeactivateFn<EditFormComponent> = (component) => {
if (component.hasUnsavedChanges()) {
return confirm('Modifications non sauvegardées. Quitter quand même ?');
}
return true;
};
confirm() natif, utilisez une modale Angular Material ou Bootstrap pour une meilleure expérience utilisateur. Le guard peut retourner un Observable<boolean> émis par la modale.
CanMatch — Contrôler le matching de route
CanMatch (introduit en Angular 14.1, remplaçant de CanLoad) détermine si une route doit être matchée avant même d'être chargée. C'est plus puissant que CanActivate car il empêche le lazy loading du bundle si la condition n'est pas remplie — parfait pour les feature flags ou les rôles utilisateurs.
// feature.guard.ts
import { inject } from '@angular/core';
import { CanMatchFn, Router } from '@angular/router';
import { FeatureFlagService } from './feature-flag.service';
export const betaFeatureGuard: CanMatchFn = (route, segments) => {
const featureService = inject(FeatureFlagService);
const router = inject(Router);
return featureService.isEnabled('betaFeature')
? true
: router.createUrlTree(['/not-found']);
};
Contrairement à CanActivate, si CanMatch retourne false, Angular continue d'essayer les autres routes correspondantes dans la configuration. Cela permet des configurations multi-routes élégantes selon le contexte.
Resolve — Pré-charger les données avant l'activation
Un Resolver pré-charge des données avant que le composant ne soit instancié. Le composant reçoit les données via ActivatedRoute.data, sans état de chargement à gérer dans le template.
// user.resolver.ts
import { inject } from '@angular/core';
import { ResolveFn } from '@angular/router';
import { User } from './user.model';
import { UserService } from './user.service';
export const userResolver: ResolveFn<User> = (route) => {
return inject(UserService).getById(route.paramMap.get('id')!);
};
// app.routes.ts
{
path: 'users/:id',
component: UserDetailComponent,
resolve: { user: userResolver }
}
// user-detail.component.ts
export class UserDetailComponent {
user = inject(ActivatedRoute).snapshot.data['user'] as User;
}
Functional Guards vs Class-based — que choisir ?
| Critère | Functional (Angular 15+) | Class-based |
|---|---|---|
| Syntaxe | Fonction avec inject() |
@Injectable + implements |
| Boilerplate | ✅ Minimal | ❌ Verbeux |
| Composition | ✅ Composable (and(), or()) |
Limitée |
| Tests | ✅ Plus simples (pas de TestBed requis) | Nécessite TestBed |
| Compatibilité | Angular 15+ | Angular 2+ |
| Recommandation | 🎯 Nouveaux projets Angular 15+ | Projets legacy |
Composition de Guards avec and() et or()
Angular 15.2+ offre les helpers and() et or() dans le package @angular/router pour composer plusieurs guards de façon déclarative, sans logique conditionnelle manuelle.
import { and, or } from '@angular/router';
import { authGuard } from './auth.guard';
import { roleGuard } from './role.guard';
import { featureGuard } from './feature.guard';
// Doit être authentifié ET avoir le rôle admin
const adminGuard = and([authGuard, roleGuard('admin')]);
// Peut être admin OU superuser
const staffGuard = or([roleGuard('admin'), roleGuard('superuser')]);
Organisation et bonnes pratiques
src/app/core/guards/auth.guard.ts— Guard d'authentificationsrc/app/core/guards/role.guard.ts— Guard de rôlessrc/app/core/guards/unsaved-changes.guard.ts— CanDeactivate formulairesrc/app/core/resolvers/user.resolver.ts— Resolver utilisateursrc/app/features/admin/guards/admin.guard.ts— Guard feature-specific
Gardez les guards simples et à responsabilité unique. Un guard qui fait trop de choses devient difficile à tester. Utilisez l'injection de dépendances pour déléguer la logique aux services (AuthService, PermissionService, FeatureFlagService).
FAQ — Guards et Resolvers Angular
CanMatch pour les modules lazy-loaded conditionnels (feature flags, rôles) et CanActivate pour la protection après chargement.
TestBed si les services injectés sont mockés correctement. Vous pouvez utiliser runInInjectionContext du package @angular/core/testing pour fournir un contexte d'injection minimal.
import { runInInjectionContext } from '@angular/core';
it('should redirect unauthenticated users', () => {
const result = runInInjectionContext(injector, () =>
authGuard(mockRoute, mockState)
);
expect(result).toBeInstanceOf(UrlTree);
});
Observable<boolean | UrlTree>. Angular attend que l'Observable se complète avant de poursuivre la navigation. Cela permet de faire des appels API asynchrones (ex: vérifier une session côté serveur). Pensez à utiliser take(1) ou first() pour s'assurer que l'Observable se complète.
CanLoad est déprécié depuis Angular 15.1. Son remplaçant direct est CanMatch, qui offre les mêmes fonctionnalités (empêcher le chargement des modules lazy) tout en étant plus flexible : il permet plusieurs routes avec le même path et différents guards selon le contexte.
ActivatedRouteSnapshot en premier argument. Vous pouvez lire les paramètres de route (route.paramMap.get('id')), les query params (route.queryParamMap) ou les données statiques (route.data) pour adapter le comportement du resolver selon la route active.
Conclusion
Les Guards et Resolvers Angular sont des outils essentiels pour construire des applications robustes et sécurisées. Avec l'introduction des functional guards en Angular 15, le code est devenu significativement plus concis et testable. Utilisez CanActivate pour l'authentification, CanDeactivate pour protéger les formulaires, CanMatch pour les feature flags avec lazy loading, et les Resolvers pour éliminer les états de chargement dans vos composants.
Ce générateur vous fournit une base de code propre et prête à l'emploi. Adaptez les noms, les services et les logiques métier selon votre architecture, et consultez la documentation officielle Angular Router pour aller plus loin.