Cours Angular · Module 4: Routing
Les gardiens des portes - les guards de route
Dans cette leçon7
Fais une seule expérience avant même de lire une ligne de théorie. Déconnecte-toi de ton application, puis tape l'adresse /admin dans la barre du navigateur et appuie sur entrée. Le poste de commandement s'ouvre sans broncher. Le routeur a fait exactement ce que tu lui as appris : il a pris le texte de la barre, trouvé l'entrée correspondante dans le tableau des routes et déposé le composant dans l'outlet. La question "qui es-tu et as-tu le droit de passer" n'a jamais été posée, car dans toute cette mécanique il n'existe encore aucun endroit où elle pourrait l'être.
Ne compte pas sur le fait de cacher le lien dans la navigation. Un lien caché disparaît de la vue, mais l'adresse reste, et une adresse se retape de mémoire, se retrouve dans les favoris ou s'échange dans un message. Une protection que l'on contourne en tapant six caractères au clavier n'est pas une protection.
À l'Académie, personne ne laisse une porte sans garde. Le mur n'a qu'un seul passage, et dans ce passage se tient un gardien : il arrête le visiteur, examine le sceau de son parchemin et prend l'une des deux décisions possibles - laisser passer ou renvoyer. Il ne rend aucune vue, ne va chercher aucune provision, ne décore pas la cour. Il répond oui ou non. En Angular, un tel gardien s'appelle un guard, et tu vas en poster trois dans cette leçon : à la porte principale celui qui vérifie si le visiteur est seulement connecté, à la porte intérieure celui qui regarde le rang, et à la poterne de sortie celui qui ne laisse personne s'en aller avec un travail inachevé.
Un gardien n'est qu'une fonction
Un guard est une fonction que tu n'appelles jamais toi-même. L'appel appartient au routeur et se produit à un instant très précis : l'adresse est déjà mise en correspondance avec une entrée du tableau des routes, mais le composant n'existe pas encore et rien n'est arrivé à l'écran. Le routeur demande son avis au gardien, puis regarde ce que la fonction a retourné. Vrai signifie "laisse passer" et le voyage continue. Faux signifie "fais demi-tour" : la navigation est annulée, le composant n'est jamais créé, et l'adresse dans la barre revient à la précédente.
Pour que TypeScript sache quelle forme de fonction attendre ici, le paquet @angular/router exporte un alias de type tout prêt, nommé CanActivateFn. Un alias de type est un nom donné d'avance à une certaine forme - ici, celle d'une fonction à deux paramètres qui retourne une décision. Quand tu en annotes ta constante après les deux-points, tu n'as plus une seule annotation à écrire à l'intérieur : TypeScript déduira lui-même le type des deux paramètres et vérifiera lui-même le type du résultat.
Retiens ce nom à la lettre près, car deux autres, qui sonnent tout aussi raisonnablement, n'existent tout simplement pas. Le paquet @angular/router n'exporte rien qui s'appelle GuardFn ni ActivateGuard - j'ai passé les deux imports au compilateur TypeScript sur Angular 19, et les deux se terminent par la même erreur d'export introuvable. Il existe en revanche le nom CanActivate, sans suffixe, et c'est là le véritable piège, car celui-là s'importe sans la moindre erreur. Il appartient à l'ancienne variante : une classe décorée qui implémentait une interface de ce nom et arrivait dans la route comme jeton d'injection. Cette interface n'a pas disparu d'Angular 19 et une classe qui l'implémente compile encore parfaitement - je l'ai vérifié au compilateur. Seulement, elle décrit une classe, pas une fonction : j'ai tenté d'annoter avec elle une constante fléchée, et TypeScript répond que le type () => boolean n'est pas assignable au type CanActivate. Les guards fonctionnels sont apparus dans Angular 14 et constituent aujourd'hui la forme recommandée ; c'est à eux que nous nous tiendrons ici. Tu annotes donc ta constante avec CanActivateFn, jamais avec CanActivate.
Commençons par un gardien qui laisse passer tout le monde - pour voir d'abord la construction elle-même, sans décision à l'intérieur. Nous nommerons le fichier selon l'usage d'Angular : d'abord le rôle, ensuite le type.
1// guards/auth.guard.ts
2import { CanActivateFn } from '@angular/router';
3
4export const authGuard: CanActivateFn = () => {
5 return true;
6};Ce gardien ne surveille encore rien, puisqu'il retourne la vérité toute nue, et surtout il ne sera pour l'instant appelé aucune fois : le fichier existe dans le projet, mais le routeur n'en sait rien. Remarque plutôt ce qu'il n'y a pas ici. Pas de classe, pas de décorateur, pas de mot new - une simple constante portant une fonction fléchée, exactement comme celles que tu écrivais en JavaScript. Pas non plus la moindre annotation sur les paramètres ni sur le résultat, car toute cette connaissance a été apportée par le CanActivateFn posé après les deux-points. Les parenthèses vides de la flèche sont parfaitement correctes : puisque tu ne te sers pas des arguments, tu n'es pas obligé de les écrire, et TypeScript autorise à fournir une fonction ayant moins de paramètres là où l'on en attend une qui en a davantage. Nous irons les chercher dans un instant, et ils apparaîtront alors.
Poster le gardien à la porte
Le gardien à lui seul n'arrêtera personne tant que tu ne lui auras pas ordonné de se tenir dans un passage précis. Cet emplacement se désigne dans l'entrée de route, par le champ canActivate. Ce champ accepte un tableau, et ce n'est pas par politesse : plusieurs gardiens peuvent se relayer à une même porte, chacun chargé d'autre chose. Le routeur les appellera dans l'ordre d'écriture et ne laissera passer le visiteur que si tous disent oui ; le premier refus clôt l'affaire, et les suivants ne sont même plus interrogés.
Observe bien l'ordre d'écriture de cette unique ligne, car tu auras à le reconstituer jeton par jeton : d'abord l'accolade ouvrante avec le chemin, ensuite le composant, ensuite le tableau des gardiens, et tout à la fin l'accolade qui referme l'entrée. Les imports comptent aussi - la constante du gardien s'importe exactement comme le composant, puisque ce n'est jamais qu'un export de plus dans ton projet.
1// app.routes.ts
2import { Routes } from '@angular/router';
3import { AdminComponent } from './admin/admin.component';
4import { authGuard } from './guards/auth.guard';
5
6export const routes: Routes = [
7 { path: 'admin', component: AdminComponent, canActivate: [authGuard] }
8];Désormais, chaque entrée sur /admin traverse le gardien - qui laisse toujours passer tout le monde, puisqu'il ne contient qu'un return true, mais qui participe enfin au cycle de vie de la route. Le détail le plus important se cache dans le tableau : le nom authGuard n'est suivi d'aucune parenthèse. Tu fournis la fonction elle-même, et non son résultat. Si tu écrivais [authGuard()], tu tenterais d'appeler le gardien immédiatement, à l'instant où le fichier des routes est chargé, et c'est son résultat qui atterrirait dans le tableau à sa place. Cette erreur-là, TypeScript la rattrape pour toi - je l'ai vérifié au compilateur : il répond que la fonction attendait deux arguments et n'en a reçu aucun. Remarque aussi ce qui est resté intact : AdminComponent. Le composant ignore que quiconque le surveille, il ne contient pas une ligne de vérification des droits, et il ne devrait pas en contenir - la décision se prend avant lui, au niveau de la route.
Demander le sceau
Un gardien qui dit oui à tout le monde n'est qu'une décoration. Un vrai gardien doit interroger quelqu'un sur l'état des choses, et le savoir sur qui est connecté ne réside ni dans le routeur ni dans le composant. Convenons que nous avons dans le projet notre propre service AuthService doté de deux méthodes : isLoggedIn() répond vrai ou faux à la question de la connexion, et hasRole() reçoit le nom d'un rang et dit si le visiteur le possède. Tu construiras l'intérieur d'un tel service dans le module consacré aux guildes d'artisans ; ici, traite-le comme un coffre fermé à deux questions.
Tu réclames ce service avec la fonction inject() du paquet @angular/core, la même que celle par laquelle tu demandais, à la leçon précédente, le parchemin de la route active. Une condition mérite toutefois d'être connue dès maintenant : inject() ne peut être appelée que dans ce qu'on nomme un contexte d'injection, c'est-à-dire à un endroit où Angular a son injecteur sous la main. Le corps d'un guard est un tel endroit, puisque c'est le routeur qui appelle cette fonction et qui prépare lui-même ce contexte. L'appel doit cependant rester synchrone, donc figurer directement dans le corps du gardien et non à l'intérieur d'une fonction déclenchée plus tard - voilà pourquoi inject() s'écrit dans les toutes premières lignes, avant que la moindre logique ne commence.
1// guards/auth.guard.ts
2import { inject } from '@angular/core';
3import { CanActivateFn } from '@angular/router';
4import { AuthService } from '../services/auth.service';
5
6export const authGuard: CanActivateFn = () => {
7 const auth = inject(AuthService);
8 return auth.isLoggedIn();
9};La porte s'est enfin mise à filtrer : le visiteur connecté passe, celui qui ne l'est pas est renvoyé et AdminComponent n'est même pas créé. Ce qui n'a pas bougé - et c'est le plus intéressant ici - c'est que l'entrée de route est rigoureusement identique à celle d'il y a un instant, que le type du gardien est le même et que le nom de la constante reste authGuard, si bien que le fichier des routes ne réclame pas la moindre retouche. Seul l'intérieur de la fonction a changé : au lieu d'une vérité constante, tu retournes maintenant la réponse d'une question posée au service.
Il reste pourtant une chose qui gâchera l'impression du côté de l'utilisateur. Quand isLoggedIn() retourne faux, le visiteur voit... rien. L'écran sur lequel il se trouvait reste en place, l'adresse revient à la précédente, aucun message n'apparaît. De son point de vue, le clic n'a tout simplement pas fonctionné, et rien ne permet de distinguer un refus d'un bouton cassé.
Renvoyer, mais en indiquant le chemin
Le gardien d'une vraie porte ne se tait pas - il renvoie le visiteur à la chancellerie pour y faire apposer un sceau. Un guard sait faire exactement la même chose, car il a le droit de retourner autre chose qu'une valeur booléenne. Il peut rendre un objet de type UrlTree, c'est-à-dire une adresse toute prête décomposée en ses parties : les segments du chemin, les paramètres de requête, le fragment. Pour le routeur, un tel retour signifie "ne le fais pas entrer ici, conduis-le plutôt là-bas", et c'est une seule opération, pas deux. Il peut également retourner un flux ou une promesse de cette réponse lorsque la décision exige d'interroger le serveur - le routeur attendra alors le résultat.
Un UrlTree se construit avec la méthode createUrlTree() du service Router, celui-là même par lequel tu donnais l'ordre de marche à la leçon précédente. La méthode accepte les mêmes arguments que navigate() : un tableau de segments et un objet d'options facultatif avec son champ queryParams. La différence est essentielle et mérite de rester en tête : navigate() exécute le déplacement sur-le-champ, tandis que createUrlTree() se contente de construire la description d'une adresse et de te la rendre. Je te recommande de retourner un UrlTree et je te déconseille d'appeler navigate() à l'intérieur d'un gardien : cet appel lance une seconde navigation alors que la première n'est pas terminée, et deux voyages menés de front savent laisser dans la barre l'adresse du perdant.
À l'adresse de connexion, nous ajouterons encore une note en marge : l'adresse que le visiteur tentait d'atteindre, pour qu'après identification il revienne là où il avait commencé. Nous la prenons dans le second paramètre du gardien, il est donc temps d'écrire enfin les deux. Le premier, traditionnellement nommé route, est l'instantané de la route de destination - le même genre d'objet que celui d'où tu lisais paramMap. Le second, state, est l'instantané de l'état complet du routeur, et il porte un champ url contenant l'adresse entière vers laquelle la navigation se dirigeait.
1// guards/auth.guard.ts
2import { inject } from '@angular/core';
3import { CanActivateFn, Router } from '@angular/router';
4import { AuthService } from '../services/auth.service';
5
6export const authGuard: CanActivateFn = (route, state) => {
7 const auth = inject(AuthService);
8 const router = inject(Router);
9
10 if (auth.isLoggedIn()) {
11 return true;
12 }
13
14 // renvoie vers la connexion et retiens ou le visiteur voulait aller
15 return router.createUrlTree(['/login'], {
16 queryParams: { returnUrl: state.url }
17 });
18};J'ai fait tourner ce gardien sur un vrai routeur Angular 19. Un visiteur non connecté qui tape /admin atterrit à l'adresse /login?returnUrl=%2Fadmin - la barre oblique de la note est encodée, car le routeur encode lui-même les caractères spéciaux dans les paramètres de requête, et c'est exactement cette valeur que l'écran de connexion relira par queryParamMap. Le visiteur connecté, lui, passe sur /admin sans le moindre changement de comportement. Rien n'a bougé au passage, ni le tableau des routes ni le type du gardien : CanActivateFn admettait depuis le début trois sortes de réponses, et nous ne faisons qu'utiliser la deuxième. Note enfin que la branche avec return true se place avant la redirection et met fin à la fonction - c'est la construction typique d'un gardien : d'abord tu libères le cas autorisé, ensuite tu t'occupes du refus.
Le rang inscrit dans l'entrée de route
Être connecté ne signifie pas encore être autorisé. Au poste de commandement entre le shogun, à l'armurerie le forgeron, et l'élève ordinaire n'entre dans aucun des deux - on pourrait croire à ce stade qu'il te faut un gardien distinct par rang. Il ne t'en faut pas. Un seul suffit, à condition qu'il reçoive dans sa consigne le nom du rang qu'il doit surveiller.
Pour transmettre une telle consigne, l'entrée de route dispose du champ data. C'est un objet ordinaire, avec tes propres clés et tes propres valeurs, accroché à la configuration de la route et visible ensuite dans l'instantané. Le nom est unique et n'admet aucun remplaçant : le type Route ne connaît pas de champ meta, pas de champ config et pas de champ params - les paramètres proviennent de l'adresse et non de la configuration, il n'y a donc rien de tel à écrire dans le fichier des routes. J'ai inscrit chacun de ces trois noms dans une route et je les ai compilés : tous les trois se soldent par le même message de TypeScript, disant que cette propriété n'existe pas dans le type Route.
Postons donc deux gardiens d'un coup à la même porte et ajoutons-leur la consigne. L'ordre dans le tableau n'est pas indifférent : nous vérifions d'abord la connexion, le rang seulement ensuite, car interroger sur son rang quelqu'un d'anonyme n'a aucun sens.
1// app.routes.ts, fragment du tableau des routes
2{
3 path: 'admin',
4 component: AdminComponent,
5 canActivate: [authGuard, roleGuard],
6 data: { role: 'shogun' }
7}L'entrée a grandi de deux lignes et d'aucun concept nouveau : canActivate reste le même tableau, avec simplement un second nom à l'intérieur, et data n'est qu'un objet ordinaire que le routeur emportera avec lui pour le remettre au gardien. AdminComponent, une fois encore, n'est au courant de rien. L'essentiel est de ne pas confondre les niveaux : data est un champ de l'entrée de route, sœur de path et de component, et non un champ situé à l'intérieur de canActivate.
De l'autre côté, le gardien doit lire cette consigne, et tout ce dont il a besoin pour cela lui arrive dans le premier paramètre. L'instantané de la route possède un champ data contenant précisément l'objet que tu as écrit dans la configuration, si bien que tu extrais le rang par l'écriture route.data['role']. Trois autres écritures viennent naturellement à l'esprit à cet endroit, et toutes trois sont fausses pour trois raisons différentes qu'il vaut la peine de connaître. route.params['role'] compilera sans broncher, mais ira consulter les paramètres du chemin, là où atterrissent les tronçons d'adresse précédés de deux-points ; aucun :role ne figure dans le chemin, tu récupéreras donc undefined et un bug silencieux. route.query['role'] ne compilera pas du tout, car l'instantané n'a aucun champ nommé query - les paramètres de requête vivent dans queryParams et dans queryParamMap, mais ce n'est de toute façon pas le bon endroit, puisque nous cherchons une consigne venue de la configuration et non une note venue de l'adresse. route.role ne compilera pas davantage : l'instantané n'a pas de champ portant le nom de ta clé, car tes clés habitent à l'intérieur de l'objet data. J'ai fait passer ces deux tentatives au compilateur et toutes deux se terminent par une erreur de propriété inexistante.
Les valeurs rangées dans data reçoivent un type aussi lâche que possible, car Angular n'a aucune idée de ce que tu y mettras. Pour continuer à traiter le rang comme du texte, nous ajouterons as string - une assurance donnée à TypeScript, appelée assertion de type, qui dit "je sais ce qu'il y a là, considère que c'est une chaîne".
1// guards/role.guard.ts
2import { inject } from '@angular/core';
3import { CanActivateFn, Router } from '@angular/router';
4import { AuthService } from '../services/auth.service';
5
6export const roleGuard: CanActivateFn = (route) => {
7 const auth = inject(AuthService);
8 const router = inject(Router);
9 const requiredRole = route.data['role'] as string;
10
11 if (auth.hasRole(requiredRole)) {
12 return true;
13 }
14
15 return router.createUrlTree(['/forbidden']);
16};Ce même gardien desservira désormais chaque porte de l'Académie - à l'armurerie tu écriras data: { role: 'forgeron' } et tu n'écriras pas une ligne de code nouvelle. J'ai fait tourner les deux routes ensemble : le visiteur au rang de shogun entre sur /admin, celui qui ne l'a pas atterrit sur /forbidden, et l'adresse /admin ne clignote même pas dans la barre. Remarque ce qui est resté comme avant : la construction du gardien est identique à celle d'authGuard - même type, même inject(), même createUrlTree() au moment du refus. Une seule ligne est nouvelle, celle qui lit la consigne. Cette fois je n'ai pas écrit le second paramètre, car l'adresse de destination ne me sert à rien ici, et TypeScript accepte une liste de paramètres plus courte.
Le gardien de la sortie
Les portes surveillent l'entrée, mais il existe une situation inverse où un gardien t'est tout aussi nécessaire. Un élève remplit un long formulaire de candidature à un clan, il en a saisi les trois quarts, puis il clique sur "Armurerie" dans la navigation. Angular fera ce qu'il a demandé : il détruira le composant du formulaire et affichera l'armurerie. Le travail est perdu, et l'utilisateur ne l'apprend qu'au moment où il est déjà trop tard.
C'est à cela que sert la seconde sorte de gardien, déclenchée non pas à l'entrée mais au moment de quitter la route. Son type s'appelle CanDeactivateFn et il ne diffère du précédent que sur un point : il reçoit dans son premier paramètre l'instance du composant qui est sur le point de disparaître. Angular ne sait pas d'avance de quel composant il s'agit, le type est donc générique - tu lui indiques entre chevrons ce que tu attends. Lis l'écriture CanDeactivateFn<NomDuType> comme "gardien de sortie pour un composant décrit par NomDuType".
Nous ne voulons pas attacher le gardien à une classe unique, car les formulaires seront nombreux dans l'application. Nous allons plutôt décrire un contrat : n'importe quel composant capable de répondre à la question des modifications non enregistrées. Pour décrire de tels contrats, TypeScript propose le mot interface - une déclaration qui dit quels champs et quelles méthodes un objet doit posséder, sans dire comment ils sont faits.
1// guards/unsaved-changes.guard.ts
2export interface CanComponentDeactivate {
3 hasUnsavedChanges: () => boolean;
4}Cette interface ne crée rien qui fonctionne et disparaît sans laisser de trace après la compilation - elle n'existe que pour donner à TypeScript quelque chose à surveiller. Lis-la ainsi : un composant qui respecte ce contrat possède une méthode hasUnsavedChanges qui ne prend aucun argument et retourne une valeur booléenne. Le composant du formulaire ajoutera donc implements CanComponentDeactivate et implémentera cette unique méthode, et le gardien n'aura besoin de rien savoir de plus à son sujet.
Passons maintenant au gardien lui-même. Quand des modifications ne sont pas enregistrées, nous voulons interroger l'utilisateur, pas décider à sa place. Le plus simple pour cela est confirm() - la fenêtre intégrée du navigateur qui pose une question avec deux boutons et retourne vrai après confirmation, faux après annulation. C'est exactement le type qu'attend le gardien, son résultat peut donc être rendu tel quel.
1// guards/unsaved-changes.guard.ts
2import { CanDeactivateFn } from '@angular/router';
3
4export const unsavedChangesGuard: CanDeactivateFn<CanComponentDeactivate> = (component) => {
5 if (component.hasUnsavedChanges()) {
6 return confirm('Tu as des modifications non enregistrees. Veux-tu vraiment quitter cet ecran ?');
7 }
8 return true;
9};La répartition des décisions est ici l'inverse de celle de la porte d'entrée, alors lis attentivement : s'il n'y a aucune modification, la fonction atteint return true et la sortie se fait sans la moindre fenêtre - un utilisateur qui n'a touché à rien ne sera interrogé sur rien. Nous ne posons la question que lorsqu'il y a quelque chose à perdre. Le vrai de confirm() signifie "laisse-le sortir", le faux signifie "qu'il reste", et sur un faux Angular interrompt la navigation, l'adresse dans la barre revenant sur la route du formulaire. Remarque ce que le gardien de sortie ne fait pas : il n'enregistre rien. Ce n'est pas un mécanisme de protection des données, seulement un avertissement - l'enregistrement reste l'affaire du composant.
Reste la dernière étape, la même qu'à la porte d'entrée : le gardien doit rejoindre l'entrée de route. Le champ s'appelle canDeactivate, c'est un tableau tout comme canActivate, et il accepte de la même manière le nom nu de la fonction, sans parenthèses.
1// app.routes.ts, fragment du tableau des routes
2{
3 path: 'samurai/:id/edit',
4 component: SamuraiEditComponent,
5 canDeactivate: [unsavedChangesGuard]
6}Dès cet instant, chaque sortie de l'écran d'édition - par un clic dans la navigation, par le bouton retour du navigateur ou par un appel à navigate() depuis le code - passe par cette question. J'ai vérifié les deux réponses sur un routeur en fonctionnement : après confirmation la navigation aboutit, après annulation l'adresse demeure sur la route d'édition et le composant du formulaire continue de vivre avec tout son contenu. Cela ne change rien au champ canActivate de cette route ni aux gardiens de l'entrée - ce sont deux postes indépendants, l'un à l'entrée, l'autre à la sortie.
Toute la garde et l'ordre du service
Les gardiens du routeur sont plus de deux, et il est facile de confondre leurs rôles tant les noms se ressemblent. Rassemblons donc toute la garde au même endroit, avec la question à laquelle chacun répond.
| Guard | Quand il intervient | À quelle question il répond |
|---|---|---|
| CanActivate | avant l'entrée sur une route | cet utilisateur peut-il accéder à ce chemin |
| CanActivateChild | avant l'entrée sur une route enfant | peut-il descendre plus bas, dans les sous-routes |
| CanDeactivate | à la tentative de quitter une route | a-t-il le droit de sortir d'ici |
| CanMatch | au moment de la mise en correspondance | faut-il seulement envisager cette entrée de route |
| Resolve | après les gardiens, avant l'activation | quelles données charger avant l'affichage |
À la question "cet utilisateur peut-il accéder à ce chemin" répond exactement l'un d'entre eux, et c'est CanActivate - les autres s'occupent d'autre chose. CanDeactivate surveille la direction inverse, il ne dira donc pas un mot à l'entrée. Resolve n'est d'ailleurs pas vraiment un gardien mais un fournisseur : il est incapable de dire "je ne te laisse pas passer", sa tâche est d'apporter les données, et tu le découvriras en détail à la leçon suivante. CanLoad mérite un mot à part, car tu le croiseras dans les tutoriels plus anciens : il surveillait le téléchargement du paquet d'une route chargée paresseusement, mais il a été remplacé par CanMatch et, dans Angular 19, son champ dans l'entrée de route est marqué comme déprécié. Ni l'un ni l'autre ne répond à la question de l'entrée sur une route déjà mise en correspondance - ils décident si cette route entre seulement en jeu.
Fixe-toi encore en tête l'ordre dans lequel tout cela se produit au cours de la vie d'une route, car sans lui l'erreur de raisonnement guette. CanActivate prend la parole en premier et décide si tu entres, tout simplement. Ce n'est qu'une fois qu'il t'a laissé passer que les resolvers partent chercher les données - et c'est sensé dans cet ordre précis, car il n'y a aucune raison de faire venir des provisions du serveur pour quelqu'un que l'on va renvoyer de devant la porte. L'écran vit ensuite sa vie, parfois longuement. Et CanDeactivate ne se manifeste qu'à la toute fin, à la tentative de sortie - quand il y a enfin quelque chose à protéger.
Souviens-toi : une porte sans gardien n'est qu'un trou dans le mur, et un gardien n'a qu'une seule tâche - dire oui ou non, et indiquer le chemin à celui qu'il vient de renvoyer.
Tu as repéré une erreur dans cette leçon ?
Teste tes connaissances
Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.
1. Quel guard utilise-t-on pour vérifier si un utilisateur peut accéder à un chemin donné ?
2. Quel type utilise-t-on pour un guard moderne (fonctionnel) protégeant l'accès à une route ?
Voici 2 des 5 questions de cette leçon. Réponds aux autres dans le jeu.
Tâches pratiques dans le jeu
- Tri horizontal
Ordonne sur une seule ligne le rattachement d'un guard à la route admin :
- Éditeur de code
Complète le guard fonctionnel qui vérifie si l'utilisateur est connecté.
- Tri vertical
Ordonne les guards selon le moment où ils s'exécutent dans le cycle de vie de la route (du premier au dernier) :