Cours Angular · Module 4: Routing

Routes imbriquées et Resolve - les salles du dojo

5 min de lecture
Dans cette leçon2

Le dojo n'est pas une seule salle - c'est un bâtiment avec des pièces à l'intérieur des pièces : la salle d'entraînement, et dedans le coin des armes, et dedans le profil d'un samouraï précis. Les routes d'Angular s'imbriquent exactement de la même façon. Dans cette leçon tu découvres deux choses : comment construire des routes dans les routes (parent et enfants) et comment utiliser un resolver pour récupérer les données avant que l'écran ne s'affiche.

Les routes enfants - des chemins dans les chemins

Une route peut avoir une propriété children - un tableau de routes qui se rendent à l'intérieur du parent. Il n'existe pas de propriété nested, et aucun RouterChildModule à importer : tout tient dans children. Regarde comment /dojo abrite sa page d'accueil et sa salle d'entraînement, et comment le profil d'un samouraï possède ses propres onglets.

1export const routes: Routes = [
2  {
3    path: 'dojo',
4    component: DojoLayoutComponent,
5    children: [
6      { path: '', component: DojoHomeComponent },
7      { path: 'training', component: TrainingComponent },
8      {
9        path: 'samurai/:id',
10        component: SamuraiProfileComponent,
11        children: [
12          { path: '', redirectTo: 'info', pathMatch: 'full' },
13          { path: 'info', component: SamuraiInfoComponent },
14          { path: 'skills', component: SamuraiSkillsComponent }
15        ]
16      }
17    ]
18  }
19];

Lis ceci comme le plan d'un bâtiment. Les chemins des enfants sont relatifs au parent : dans children tu écris 'training' et non '/dojo/training', c'est Angular qui recolle les morceaux. Ainsi /dojo/training est la salle d'entraînement du dojo, et /dojo/samurai/5/skills l'onglet des compétences dans le profil du samouraï numéro 5. Remarque deux astuces : le chemin vide { path: '' } est la vue par défaut du parent (quoi afficher quand personne n'a choisi de sous-route), et redirectTo: 'info' renvoie du profil droit vers l'onglet info, pour que l'écran ne soit jamais vide. Un seul fichier de routes suffit - pas besoin d'un fichier séparé pour chaque niveau.

Pour que les enfants aient un endroit où se rendre, le parent doit avoir son propre router-outlet :

1@Component({
2  template: `
3    <header>
4      <h1>Dojo</h1>
5      <nav><a routerLink="training">Entrainement</a></nav>
6    </header>
7
8    <router-outlet></router-outlet>
9  `
10})
11export class DojoLayoutComponent { }

C'est le cœur de l'imbrication : DojoLayoutComponent dessine le cadre commun (en-tête, navigation), et <router-outlet> est la fenêtre où apparaît l'enfant actif. Tu vas sur /dojo/training - TrainingComponent se rend dans cette fenêtre, mais l'en-tête du dojo reste en place. Les composants des routes enfants s'affichent donc dans un <router-outlet> placé à l'intérieur du template du composant parent : pas directement dans app.component, et la balise <child-outlet> n'existe tout simplement pas. Chaque niveau d'imbrication a son outlet, comme une pièce dans une pièce.

Le resolver - les données avant d'entrer

Par défaut, le composant se charge immédiatement et tu vas chercher les données dans ngOnInit - pendant un instant l'écran reste vide. Le resolver inverse l'ordre : il récupère les données avant l'activation de la route, si bien que le composant dispose de tout dès la première image. Ce n'est pas un outil pour résoudre des conflits entre routes, ni pour valider les paramètres de l'URL, ni pour faire des redirections conditionnelles - cela reste le travail des guards et de redirectTo.

1import { ResolveFn } from '@angular/router';
2
3export const samuraiResolver: ResolveFn<Samurai> = (route) => {
4  const service = inject(SamuraiService);
5  const id = route.paramMap.get('id')!;
6  return service.getById(+id);
7};
8
9// Dans la definition de la route
10{
11  path: 'samurai/:id',
12  component: SamuraiDetailComponent,
13  resolve: { samurai: samuraiResolver }
14}

Un resolver moderne est une simple fonction typée ResolveFn<T> - attention à l'orthographe, c'est ResolveFn et non ResolverFn, et l'ancienne approche par classe implémentant l'interface Resolve appartient au passé. Ici la fonction va chercher le samouraï par son id lu dans l'adresse, en récupérant le service avec inject importé de @angular/core. Angular attend que les données arrivent, et seulement ensuite affiche le composant. Tu ne verras donc jamais clignoter un profil vide. Dans le composant, tu reçois les données prêtes à l'emploi directement depuis la route :

1export class SamuraiDetailComponent {
2  private route = inject(ActivatedRoute);
3  samurai = this.route.snapshot.data['samurai'] as Samurai;
4}

La clé samurai dans data est exactement le nom que tu as donné dans resolve: { samurai: ... } - on lit donc this.route.snapshot.data['samurai'], surtout pas snapshot.params['samurai'] qui ne contient que les paramètres de l'URL, ni this.router.data qui n'existe pas sur le Router. Au lieu d'appeler le service et de gérer un état de chargement dans le composant, tu lis simplement l'objet déjà prêt. Le compromis est assumé : l'utilisateur patiente un instant de plus pendant la transition, mais il ne voit pas d'écran vide - idéal pour les pages qui n'ont aucun sens sans leurs données.

Retiens deux choses de cette leçon : les routes s'imbriquent grâce à children et se rendent dans le router-outlet du parent, comme des pièces dans le dojo ; et le resolver récupère les données avant l'entrée, pour que le composant ne démarre jamais vide.

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. Comment définit-on des routes imbriquées dans Angular ?

  2. 2. Où s'affichent les composants des routes enfants ?

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

Tâches pratiques dans le jeu

  • Tri vertical

    Ordonne les lignes de la définition d'une route parente avec des enfants (de haut en bas, comme dans l'éditeur) :

  • Éditeur de code

    Complète la configuration avec des routes imbriquées pour DashboardComponent.

  • Clique dans l'ordre

    Clique dans l'ordre pour construire la propriété resolve qui attache un resolver sous la clé 'samurai' :

Articles utiles