Cours Angular · Module 4: Routing

Les étapes du Tōkaidō - le routage dans Angular

22 min de lecture
Dans cette leçon13

Ton application a grandi. Il y a une liste de guerriers, il y a l'écran du dojo, il y a l'armurerie, et tu passes de l'un à l'autre comme tu l'as appris en étudiant l'anatomie d'un composant : un champ dans la classe, quelques blocs @if dans le template, un clic et la vue change. Au premier regard, tout tient debout.

Essaie pourtant d'envoyer l'écran de l'armurerie à quelqu'un. Tu copies l'adresse depuis la barre du navigateur, tu la colles dans un message, ton camarade l'ouvre - et il atterrit sur l'écran de départ, parce que dans la barre il n'y a jamais eu qu'une seule et même adresse. Appuie sur la flèche retour : au lieu de revenir à la vue précédente, tu sors de l'application. Recharge la page : tu repars du tout début, car ce champ de la classe ne vivait que dans la mémoire. La seule chose que l'utilisateur met en favori et transmet aux autres, c'est-à-dire l'adresse, ne sait absolument rien de ton application.

À l'Académie, personne ne marche au jugé. Entre Edo et Kyoto court le Tōkaidō - la grande route découpée en étapes, et à chaque étape se dresse un panneau portant son nom. Tu donnes à quelqu'un le nom d'une étape, et il arrive exactement là où toi tu te tenais. Le routage fabrique cette route-là à partir de ta barre d'adresse : à chaque adresse tu associes un écran, et Angular veille à ce que l'adresse et la vue racontent toujours la même chose. La flèche retour se met à reculer dans ton application, le rechargement te ramène là où tu étais, et l'adresse copiée conduit ton camarade devant la bonne porte.

Ce que tu vas apprendre

  • dessiner la carte de la route dans un tableau de routes
  • remettre cette carte à Angular avec une seule fonction dans le fichier de configuration
  • désigner dans le template la porte par laquelle entre l'écran de la route courante
  • guider l'utilisateur avec des liens et mettre en évidence l'étape où il se trouve
  • fixer l'étape par défaut ainsi que l'étape réservée aux égarés
  • marquer dans un chemin le segment variable, par exemple le numéro d'un guerrier
  • renvoyer un écran dans un paquet séparé, téléchargé seulement à la demande

La carte de la route - le tableau des routes

Une route est une paire : sous quelle adresse, et quoi montrer à ce moment-là. Angular garde ces paires dans un tableau tout à fait ordinaire, dont chaque élément est un objet tout aussi ordinaire. On donne à ce tableau le type Routes - un nom exposé par le paquet @angular/router, autrement dit une promesse faite au compilateur qu'à l'intérieur ne se trouveront que des routes correctement construites. Deux champs comptent dès la première minute. Le champ path est le fragment d'adresse, écrit sans barre oblique au début, car les barres obliques, le routeur les ajoute lui-même. Le champ component est la classe du composant qu'il faut afficher lorsque l'adresse correspond. Convenons du fichier app.routes.ts et commençons par les deux étapes les plus simples.

1// app.routes.ts
2import { Routes } from '@angular/router';
3import { HomeComponent } from './home.component';
4import { SamuraiComponent } from './samurai.component';
5
6export const routes: Routes = [
7  { path: 'home', component: HomeComponent },
8  { path: 'samurai', component: SamuraiComponent }
9];

Le fichier est prêt et pour l'instant il ne fait absolument rien - c'est la phrase la plus importante de ce paragraphe. Il en sort un tableau ordinaire de deux objets, que tu pourrais afficher dans la console et examiner comme n'importe quelle autre donnée. Aucun composant n'a été modifié, aucun écran n'a été redessiné, et le navigateur n'est au courant de rien. Remarque trois détails. Le nom Routes doit venir de @angular/router, car seul ce paquet l'exporte. Il n'y a pas de barre oblique devant home, et il ne doit pas y en avoir. Enfin, la correspondance se fait sur des segments entiers de l'adresse et non sur des expressions régulières : l'adresse /homepage n'atteindra donc jamais la route home, ce sont deux segments différents malgré leur début commun.

Remettre la carte - provideRouter

Une carte qui dort dans un tiroir n'a encore conduit personne nulle part. Il faut la remettre à Angular au moment où l'application démarre, et cet endroit, tu le connais depuis le premier module : le fichier main.ts appelle bootstrapApplication et lui passe l'objet de configuration venu de app.config.ts. Cet objet porte le type ApplicationConfig et possède un seul champ qui nous intéresse ici - le tableau providers, c'est-à-dire la liste des choses que l'application doit avoir sous la main pendant toute sa durée de vie. Le routeur s'ajoute à cette liste grâce à la fonction provideRouter, importée depuis @angular/router. Tu lui passes ton tableau de routes et elle te rend un jeu d'entrées prêt à figurer dans providers.

Retiens ce nom à la lettre près, car trois autres sonnent tout aussi crédibles et aucun ne fonctionnera. Les fonctions registerRoutes et useRouter n'existent tout simplement pas : le paquet @angular/router n'exporte rien de tel, et le compilateur rejette ces deux imports avec un message d'export introuvable. J'ai passé les deux au compilateur TypeScript sur Angular 19, alors ne compte pas ici sur une coquille dans la documentation. Le troisième nom est plus retors : RouterModule.forRoot(routes) existe pour de bon et tu l'as croisé dans le module sur les clans, sauf qu'il appartient à l'ancien monde des modules NgModule. Sa place est dans le tableau imports du décorateur @NgModule, pas dans le providers d'un objet ApplicationConfig - les types ne concordent pas et le compilateur s'en aperçoit. Dans une application standalone, celle que tu construis depuis le premier module, l'enregistrement du routeur prend exactement une seule forme.

1// app.config.ts
2import { ApplicationConfig } from '@angular/core';
3import { provideRouter } from '@angular/router';
4import { routes } from './app.routes';
5
6export const appConfig: ApplicationConfig = {
7  providers: [
8    provideRouter(routes)
9  ]
10};

À partir de cet instant le routeur est vivant : il écoute la barre d'adresse, la relit après chaque changement et cherche dans ton tableau la route qui correspond. Ce qui n'a pas changé, en revanche, c'est ce que l'on voit à l'écran - toujours rien. Le routeur sait déjà quel composant il faudrait montrer, mais personne ne lui a dit où le dessiner. Remarque aussi que app.routes.ts est resté intact : le tableau de routes ignore l'existence de la configuration, et la configuration ne fait que l'importer. Cette séparation des rôles est volontaire et très commode, car on peut remplacer la carte sans jamais toucher au démarrage de l'application.

La porte du château - router-outlet

Le maillon qui manque, c'est la porte : l'endroit du template par lequel l'écran de la route courante entre à l'intérieur. Angular fournit pour cela une directive nommée RouterOutlet. Une directive n'est ni un composant ni un service, c'est un supplément que tu ajoutes au template - ici, la balise <router-outlet></router-outlet>. Le routeur repère cette balise et insère juste à côté d'elle le composant de la route trouvée. Voilà tout son rôle : elle désigne l'endroit où Angular affiche le composant du chemin courant.

Autant écarter tout de suite trois idées voisines, car chacune semble raisonnable et chacune est fausse. Premièrement, <router-outlet> ne sert pas à afficher les erreurs de routage - l'écran "page introuvable" est un composant à toi, branché sur une route, et nous y venons dans un instant. Deuxièmement, ce n'est pas une directive de redirection : une redirection est un champ d'une entrée du tableau de routes, pas une balise dans un template. Troisièmement, ce n'est pas un service de navigation : le service qui porte le nom Router et à qui l'on ordonne un déplacement depuis le code, tu le rencontreras à l'étape suivante. La porte se voit dans le template, donc RouterOutlet doit figurer dans le tableau imports du composant standalone, exactement comme tout autre élément employé dans un template.

1// app.component.ts
2import { Component } from '@angular/core';
3import { RouterOutlet } from '@angular/router';
4
5@Component({
6  selector: 'app-root',
7  standalone: true,
8  imports: [RouterOutlet],
9  template: `
10    <main>
11      <router-outlet></router-outlet>
12    </main>
13  `
14})
15export class AppComponent { }

Maintenant, entrer l'adresse /home affiche réellement HomeComponent, et entrer /samurai le remplace par SamuraiComponent. Le plus intéressant reste pourtant ce qui ne se produit pas. AppComponent lui-même n'est pas recréé : il vit sans interruption, si bien que la balise <main>, et avec elle tout en-tête ou pied de page que tu ajouterais autour de la porte, reste à l'écran sans broncher. Le navigateur ne recharge pas la page, il n'y a pas de clignotement blanc, pas de nouveau téléchargement de l'application. Seul ce petit fragment près de la porte est échangé.

Puisque les adresses fonctionnent, il serait bon de donner à l'utilisateur quelque chose à cliquer. Le réflexe souffle un lien ordinaire avec l'attribut href, et il fonctionnera d'ailleurs - mais de façon catastrophique. Le navigateur traitera href comme la demande d'un document entièrement neuf : il abandonnera toute l'application, la retéléchargera, la relancera depuis zéro et effacera tout ce que tu gardais en mémoire. Au lieu d'une transition fluide, tu obtiendras un rechargement complet. C'est pourquoi les liens internes à l'application passent par la directive routerLink, qui intercepte le clic, retient le navigateur et confie l'affaire au routeur. Elle aussi doit rejoindre le tableau imports, mais sous son nom de classe : RouterLink avec deux majuscules dans la ligne d'import, alors que dans le template on écrit routerLink avec une minuscule au début. Il existe encore une seconde voie - la méthode navigate du service Router, appelée depuis le code de la classe - mais c'est une méthode, pas une directive de template, et nous nous en occuperons à l'étape suivante.

1// app.component.ts
2import { Component } from '@angular/core';
3import { RouterLink, RouterOutlet } from '@angular/router';
4
5@Component({
6  selector: 'app-root',
7  standalone: true,
8  imports: [RouterOutlet, RouterLink],
9  template: `
10    <nav>
11      <a routerLink="/home">Accueil</a>
12      <a routerLink="/samurai">Guerriers</a>
13    </nav>
14
15    <main>
16      <router-outlet></router-outlet>
17    </main>
18  `
19})
20export class AppComponent { }

Un clic sur "Accueil" change désormais l'adresse de la barre en /home et remplace le contenu de la porte, tandis que la flèche retour ramène à la route précédente. Observe la construction d'un tel lien, car tu l'assembles toujours avec les mêmes morceaux dans le même ordre : l'ouverture de la balise <a, puis l'attribut routerLink="/home", puis le > qui ferme la balise ouvrante, puis le texte visible, et enfin </a>. Rien n'a bougé au passage dans app.routes.ts ni dans la configuration - la carte de la route est la même, tu n'as ajouté que les panneaux qui pointent vers elle.

Le panneau allumé - routerLinkActive

La navigation fonctionne, mais elle souffre d'un défaut visible à l'œil nu : tous les panneaux se ressemblent et l'utilisateur ne sait pas à quelle étape il se trouve. Angular règle cela avec la directive routerLinkActive. Tu lui donnes un nom de classe CSS et elle ajoute cette classe au lien pendant tout le temps où la route du routerLink voisin est active, puis la retire dès que ce n'est plus le cas. Il y a un piège : par défaut la comparaison se fait sur le début de l'adresse, si bien que le lien vers /samurai reste allumé même lorsque tu te trouves sur /samurai/7. Parfois c'est exactement ce que tu veux, parfois non. Tu pilotes ce comportement avec l'option routerLinkActiveOptions et son champ exact : la valeur true exige que l'adresse entière concorde. On l'écrit entre crochets, c'est-à-dire avec la liaison de propriété que tu as découverte en étudiant la communication entre guerriers - les crochets disent à Angular que les guillemets contiennent une expression à évaluer, ici un objet, et non un texte brut. Note bien l'ordre à l'intérieur de la balise : les attributs, routerLink d'abord puis routerLinkActive, se placent tous avant le > qui ferme la balise ouvrante, et le texte visible ne vient qu'après lui.

1<nav>
2  <a routerLink="/home"
3     routerLinkActive="active"
4     [routerLinkActiveOptions]="{ exact: true }">
5    Accueil
6  </a>
7  <a routerLink="/samurai" routerLinkActive="active">
8    Guerriers
9  </a>
10</nav>

Après ce changement, le lien vers la liste des guerriers reçoit la classe active y compris sur la route /samurai/7, tandis que le lien vers l'accueil ne s'allume que sur /home tout court. Attention à deux choses qui ne se sont pas produites. Angular n'a donné aucune apparence à cette classe : le mot active est un nom que tu inventes toi-même et que tu dois décrire dans ta feuille de styles, sinon tu ne verras rien du tout. Et rien n'a changé non plus dans le tableau de routes : la mise en évidence est une affaire de template et de rien d'autre. Pense également à ajouter RouterLinkActive au tableau imports du composant, à côté de RouterLink et de RouterOutlet.

L'étape par défaut - le chemin vide

Reste la question par laquelle commence chaque utilisateur : que voit celui qui a tapé le nom de domaine tout seul ? L'adresse est alors vide, et le chemin vide n'existe pas dans ta carte. On l'écrit path: '' et deux issues s'offrent à toi. Tu peux lui accrocher un composant, comme à n'importe quelle autre route, c'est-à-dire écrire { path: '', component: HomeComponent } et disposer d'un écran d'accueil sous le domaine nu. Tu peux aussi renvoyer l'utilisateur ailleurs avec le champ redirectTo, dans lequel tu indiques l'adresse de destination. Avec une redirection depuis le chemin vide apparaît un troisième champ, pathMatch, et c'est là que se cache toute la subtilité. Sa valeur par défaut est 'prefix', autrement dit "il suffit que l'adresse commence ainsi" - or une chaîne vide commence toutes les adresses du monde, et une telle route intercepterait donc littéralement tout. Angular connaît ce piège et refuse qu'on le tende : une route à chemin vide munie de redirectTo mais dépourvue de pathMatch est rejetée comme configuration invalide, avant même la première navigation, et le message d'erreur réclame explicitement ce champ manquant. La valeur 'full' signifie "tout le reste de l'adresse doit être vide" et elle seule a du sens ici. Tu composes la route de quatre morceaux dans cet ordre : l'ouverture { path: '',, puis redirectTo: '/home',, puis pathMatch: 'full', et pour finir l'accolade fermante }.

1// app.routes.ts, memes imports que plus haut
2export const routes: Routes = [
3  { path: '', redirectTo: '/home', pathMatch: 'full' },
4  { path: 'home', component: HomeComponent },
5  { path: 'samurai', component: SamuraiComponent }
6];

Entrer le domaine seul conduit maintenant à /home, et c'est bien /home qui s'affiche dans la barre. L'ensemble ne compte que pour une seule navigation, si bien que l'historique du navigateur ne conserve pas d'entrée distincte pour l'adresse vide - je l'ai vérifié, et après un tel passage il s'ajoute exactement une entrée, celle de la destination. La flèche retour n'a donc aucune chance de tourner en rond entre l'adresse vide et la cible de la redirection. La route home n'a pas bougé pour autant : elle reste une étape ordinaire avec son composant et ignore tout de la redirection. Retiens encore l'exception qui t'épargnera des lignes inutiles : quand le chemin vide mène directement à un composant, et non à une redirection, le champ pathMatch n'est pas nécessaire, car de toute façon le routeur ne considère pas la route comme trouvée tant qu'il reste dans l'adresse un segment non consommé.

Les égarés sur la route - la route wildcard

Tôt ou tard, quelqu'un tapera une adresse absente de ta carte : une faute de frappe dans un lien, un vieux favori, un utilisateur curieux. Sans préparation, cela se termine par un écran vide et une exception dans la console - le routeur dit franchement qu'il ne parvient à faire correspondre aucune route, et il interrompt la navigation. C'est pourquoi on place à la fin de la carte l'étape des égarés, appelée en anglais wildcard. Son chemin est '**', c'est-à-dire deux astérisques, et il correspond à n'importe quel nombre de segments restants. Trois autres écritures ne fonctionneront pas, et il vaut la peine de savoir pourquoi. Les chemins '404' et 'notfound' sont des routes parfaitement banales, qui ne correspondront qu'aux adresses /404 et /notfound - or ce ne sont évidemment pas ces adresses-là qui nous font trébucher. L'astérisque unique '*' n'a en Angular aucune signification magique : le routeur le prendra pour un segment ordinaire portant le nom * et ne reconnaîtra que l'adresse littérale /*. L'ordre n'est pas libre non plus, car le routeur lit le tableau de haut en bas et retient la première correspondance - un wildcard placé plus haut avalerait tout ce qui se trouve en dessous de lui.

1// app.routes.ts, memes imports plus NotFoundComponent
2export const routes: Routes = [
3  { path: '', redirectTo: '/home', pathMatch: 'full' },
4  { path: 'home', component: HomeComponent },
5  { path: 'samurai', component: SamuraiComponent },
6  { path: '**', component: NotFoundComponent }
7];

L'adresse /kyoto affiche désormais ton écran "page introuvable" au lieu d'une exception, et toutes les routes précédentes fonctionnent exactement comme avant, parce que le wildcard n'a sa chance que lorsque rien au-dessus de lui n'a correspondu. Note aussi ce que le wildcard ne fait pas : il ne change pas le code de réponse du serveur et ne constitue pas un vrai 404 au sens du protocole - c'est un écran ordinaire de ton application. Si tu ne veux pas fabriquer de composant dédié, le wildcard peut simplement renvoyer l'utilisateur au départ avec l'écriture { path: '**', redirectTo: '' }, et l'égaré atterrira là où mène la route par défaut.

Le segment variable - le paramètre de chemin

Ton clan compte trois cents guerriers et chacun possède son propre écran de profil. Personne n'écrira trois cents entrées dans un tableau, et s'il le faisait, il devrait rouvrir le fichier à chaque nouvelle recrue. Les adresses /samurai/7 et /samurai/128 ne diffèrent pourtant que par leur dernier segment. Angular permet de déclarer ce segment variable : il suffit de poser deux-points devant son nom. L'écriture samurai/:id se lit "le segment samurai, puis n'importe quoi, et appelons ce quelque chose id". Méfie-toi des écritures venues d'autres écosystèmes, car les doigts les tapent tout seuls. Les accolades {id} te viennent des serveurs du genre Express, les crochets [id] du routage par fichiers de Next.js, et $id des modèles de texte. En Angular, aucun d'eux n'est un paramètre : le routeur prendra ces écritures au pied de la lettre et cherchera un segment nommé {id} ou [id].

1// app.routes.ts, memes imports plus SamuraiDetailComponent
2export const routes: Routes = [
3  { path: 'samurai', component: SamuraiComponent },
4  { path: 'samurai/:id', component: SamuraiDetailComponent },
5  { path: '**', component: NotFoundComponent }
6];

Une seule route dessert maintenant tous les guerriers à la fois : /samurai/7 et /samurai/128 tombent sur la même entrée et affichent le même composant. La route samurai sans numéro n'en a pas souffert - la liste fonctionne toujours, car une adresse privée de ce dernier segment ne correspond qu'à elle. Il y a toutefois une chose que ce code ne fait pas, et c'est la phrase la plus importante de cette section : le routeur ne dépose pas le numéro dans le composant. Pour l'instant, SamuraiDetailComponent ne sait absolument pas s'il a été ouvert pour le septième guerrier ou pour le cent vingt-huitième. Lire cette valeur sera le sujet de la prochaine leçon.

Des troupes à la demande - loadComponent

Tous les composants cités jusqu'ici dans la carte ont un point commun : ils sont importés en haut du fichier, donc ils atterrissent dans le premier paquet que le navigateur télécharge au démarrage. Avec trois écrans cela n'a aucune importance, avec trente l'utilisateur attend du code qu'il ne verra peut-être jamais. À la place du champ component, tu peux indiquer le champ loadComponent - une fonction qui n'ira chercher le fichier qu'une fois la route atteinte, et qui en renverra la classe. Ce travail est confié à l'expression import() avec ses parenthèses, qui charge le fichier pendant l'exécution de l'application et rend une promesse, c'est-à-dire une Promise. De cette promesse nous extrayons la classe avec la méthode then, à laquelle nous passons la courte fonction c => c.ForgeComponent. Deux noms voisins sont des pièges : lazyComponent n'existe pas et le compilateur rejettera une telle entrée comme champ inconnu, tandis que le simple component fonctionne, mais fait exactement l'inverse - il embarque le code tout de suite.

1// app.routes.ts, extrait du tableau des routes
2  {
3    path: 'forge',
4    loadComponent: () => import('./forge/forge.component')
5      .then(c => c.ForgeComponent)
6  },

Du point de vue de l'utilisateur, rien n'a changé : l'adresse /forge mène au même écran, le panneau routerLink se comporte de la même façon, le wildcard attrape toujours les égarés. Seul le moment du téléchargement du code a changé - le paquet de la forge descend du serveur à la première entrée sur la route seulement, et le démarrage de l'application s'en trouve allégé d'autant. Remarque aussi qu'en haut du fichier il n'y a aucun import de ForgeComponent. Si tu l'ajoutais, la classe reviendrait dans le paquet principal et tout le bénéfice s'évaporerait.

Toute une section à la demande - loadChildren

L'armurerie n'est pas un écran unique mais une section entière : l'inventaire des katanas, la fiche d'une arme précise, le formulaire de commande chez le forgeron. Les renvoyer un par un serait fastidieux, et par la même occasion on aimerait garder leurs routes dans un fichier séparé, plus près de la section elle-même. C'est à cela que sert le champ loadChildren, jumeau de loadComponent, avec une seule différence : au lieu d'une classe de composant, tu extrais du fichier un tableau de routes. Commençons donc par ce fichier. Il exportera une constante que je nomme WEAPONS_ROUTES - le nom est libre, mais la carte principale ira le chercher plus tard à la lettre près, donc choisis-le une fois pour toutes. À l'intérieur, les chemins se comptent relativement au parent, si bien que le chemin vide désigne ici l'entrée dans l'armurerie elle-même.

1// weapons/weapons.routes.ts
2import { Routes } from '@angular/router';
3import { WeaponsListComponent } from './weapons-list.component';
4import { WeaponDetailComponent } from './weapon-detail.component';
5
6export const WEAPONS_ROUTES: Routes = [
7  { path: '', component: WeaponsListComponent },
8  { path: ':id', component: WeaponDetailComponent }
9];

Voilà une deuxième carte, plus petite - entièrement autonome et qui n'est encore reliée à rien. Elle ne contient aucun provideRouter, car celui-ci ne s'appelle qu'une fois pour toute l'application, et elle ne répète pas le segment weapons, car ce sont les routes du parent qui le colleront devant. Il ne reste qu'à pointer vers elle depuis la carte principale. L'adresse /weapons tombera alors sur le chemin vide du fichier que tu viens d'écrire, c'est-à-dire sur l'inventaire des armes, et /weapons/12 sur la fiche d'un katana précis.

1// app.routes.ts, extrait du tableau des routes
2  {
3    path: 'weapons',
4    loadChildren: () => import('./weapons/weapons.routes')
5      .then(r => r.WEAPONS_ROUTES)
6  },

Le recollage des segments se fait tout seul : le routeur retire weapons de l'adresse et confie le reste à la carte plus petite. Rien n'a changé dans le fichier de l'armurerie - le même tableau fonctionnerait sous un autre segment si tu modifiais un seul mot ici, et c'est tout l'intérêt de ce découpage. Quand employer quoi ? Je te recommande une règle simple : un écran isolé, sans sous-pages à lui, renvoie-le avec loadComponent, car l'écriture est plus courte et ne multiplie pas les fichiers ; une section entière, avec sa propre division en écrans, renvoie-la avec loadChildren, car ses routes habitent alors auprès de son code.

La carte complète

Il est temps de tout rassembler dans un seul fichier, car c'est seulement alors qu'apparaît le point capital - l'ordre. Le routeur lit le tableau de haut en bas et s'arrête à la première route qui correspond : les entrées précises doivent donc précéder les entrées générales, et le wildcard se tient toujours tout en bas. La redirection depuis le chemin vide se place en tête, pour sauter aux yeux dès la lecture du fichier.

1// app.routes.ts
2import { Routes } from '@angular/router';
3import { HomeComponent } from './home.component';
4import { SamuraiComponent } from './samurai.component';
5import { SamuraiDetailComponent } from './samurai-detail.component';
6import { NotFoundComponent } from './not-found.component';
7
8export const routes: Routes = [
9  { path: '', redirectTo: '/home', pathMatch: 'full' },
10  { path: 'home', component: HomeComponent },
11  { path: 'samurai', component: SamuraiComponent },
12  { path: 'samurai/:id', component: SamuraiDetailComponent },
13  {
14    path: 'forge',
15    loadComponent: () => import('./forge/forge.component')
16      .then(c => c.ForgeComponent)
17  },
18  {
19    path: 'weapons',
20    loadChildren: () => import('./weapons/weapons.routes')
21      .then(r => r.WEAPONS_ROUTES)
22  },
23  { path: '**', component: NotFoundComponent }
24];

Lis cette carte comme le tableau des étapes le long de la route : l'adresse vide renvoie à l'accueil, quatre segments ont un écran attitré, deux autres ne descendent qu'à la demande, et la dernière entrée ramasse quiconque s'est égaré. Observe aussi ce qui n'y figure pas. Il n'y a pas le moindre import pour la forge ni pour l'armurerie, puisque celles-ci ne descendent qu'à la demande. Il n'y a pas non plus la moindre mention d'apparence, ni de porte, ni de panneaux - la carte dit uniquement "telle adresse, tel écran", et le reste appartient au template et au fichier de configuration.

Anatomie d'une adresse

Pour finir, regarde l'adresse elle-même, car elle transporte plus d'un type d'information et tu apprendras à l'en extraire dans les prochaines leçons. Les segments séparés par des barres obliques sont obligatoires et participent à la recherche de la route - ce sont eux qui décident de l'écran qui s'ouvrira. Après le point d'interrogation viennent des mentions facultatives, qui ne participent pas à la recherche et conviennent bien au tri ou aux filtres. Après le croisillon se tient le fragment, c'est-à-dire la désignation d'un endroit à l'intérieur de l'écran déjà ouvert.

Partie de l'adresseExempleRôle
Segment de chemin/samurai/17Obligatoire, choisit la route
Mention après le point d'interrogation/samurai?rank=hatamotoFacultative, ne change pas la route
Fragment après le croisillon/samurai#bushidoDésigne un endroit dans l'écran ouvert

Aujourd'hui tu as tracé la route et dressé la porte - lire ce que transporte l'adresse elle-même nous occupera à l'étape suivante. Souviens-toi : la carte des routes ne dit que "telle adresse, tel écran", et tout le reste ne commence qu'une fois que tu te tiens à la bonne étape.

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. 1. Qu'est-ce que <router-outlet> dans Angular ?

  2. 2. Quelle fonction enregistre le routeur dans app.config.ts (Angular standalone) ?

Voici 2 des 6 questions de cette leçon. Réponds aux autres dans le jeu.

Tâches pratiques dans le jeu

  • Éditeur de code

    Complète la configuration du routage avec des routes vers HomeComponent et SamuraiComponent.

  • Clique dans l'ordre

    Clique dans l'ordre pour construire une balise <a> avec un lien de routage mis en évidence quand il est actif :

  • Tri horizontal

    Ordonne sur une seule ligne la route de redirection par défaut vers /home :

Articles utiles