Skip to content

Vous souhaitez recevoir de l'aide sur ce sujet ? rejoignez la communauté Angular.fr sur Discord.

Router Resources : le chargement de données proposé pour Angular 22.2 (11 septembre 2026)

Des routes de données parallèles se rejoignent

Angular prépare une nouvelle manière de charger les données associées à une route : les Router Resources. Cette approche prolonge la Resource API, désormais stable, en l'intégrant au routeur.

L'objectif est simple : décrire les données nécessaires à une page comme des Resource réactives, puis laisser le routeur les démarrer ensemble. Cela répond notamment à une limite des resolvers classiques : dans une hiérarchie de routes, ils peuvent provoquer une succession de chargements plutôt qu'un chargement parallèle.

Attention au statut de la fonctionnalité

Les Router Resources sont une Developer Preview dans Angular 22.2.0-next.7. Angular 22.1 est la version stable au moment de cet article. Une Developer Preview est fonctionnelle, mais son API peut changer sans respecter les garanties habituelles de compatibilité. Il s'agit donc d'une piste à tester, pas encore d'une base à imposer dans une application de production.

Le problème : les données d'une page ne viennent pas toujours d'un seul endroit

Prenons une page de détail. Sa route parente charge les informations du projet et sa route enfant charge la tâche demandée. Avec des resolvers classiques, le routeur doit parfois attendre la donnée du parent avant de commencer la résolution de l'enfant.

Si le projet prend deux secondes et la tâche trois secondes, l'attente peut atteindre cinq secondes. Le problème n'est pas le resolver lui-même : c'est la dépendance temporelle créée par la hiérarchie des routes.

Les Router Resources donnent au routeur une vision des ressources à charger. Il peut alors les commencer en parallèle. Dans notre exemple, le temps d'attente se rapproche de celui de la ressource la plus lente, soit trois secondes, à condition que les deux requêtes soient réellement indépendantes.

Ce n'est pas une promesse de gain automatique : si la requête enfant a besoin du résultat exact de la requête parente, elles doivent naturellement rester séquentielles. Mais pour les besoins indépendants d'une page, cette organisation évite un waterfall — une cascade où chaque étape attend la précédente.

Une Resource en deux mots

Une Resource représente une lecture asynchrone sous forme de Signals. Elle expose la valeur, l'état de chargement et une éventuelle erreur. resource() est l'API générique ; httpResource() est adaptée aux requêtes HTTP Angular ; rxResource() permet de partir d'un Observable existant.

Une Resource est conçue pour lire une donnée. Elle peut annuler un chargement devenu obsolète quand ses paramètres changent. Il ne faut donc pas l'utiliser pour envoyer un formulaire, supprimer un élément ou déclencher un paiement : ces mutations doivent rester des appels explicites à HttpClient ou à un service métier.

Activer les Router Resources

Dans Angular 22.2.0-next.7, il faut ajouter withRouterResources() à la configuration du routeur. withComponentInputBinding() est recommandé pour que le routeur fournisse automatiquement les résultats aux inputs du composant.

ts
import { provideRouter, withComponentInputBinding, withRouterResources } from '@angular/router';

bootstrapApplication(AppComponent, {
  providers: [
    provideRouter(
      routes,
      withComponentInputBinding(),
      withRouterResources(),
    ),
  ],
});

Une route peut alors déclarer une fonction resources. Cette fonction reçoit notamment les paramètres de route sous forme de Signal. Quand :id change alors que le composant est réutilisé, la Resource réagit et relance son chargement.

ts
import { Component, input, resource } from '@angular/core';
import { Routes } from '@angular/router';

type User = {
  id: string;
  name: string;
};

function userResource(id: () => string | undefined) {
  return resource({
    params: id,
    loader: async ({ params }) => {
      const response = await fetch(`/api/users/${params}`);

      if (!response.ok) {
        throw new Error('Utilisateur introuvable');
      }

      return response.json() as Promise<User>;
    },
  });
}

@Component({ template: `<h1>{{ user().name }}</h1>` })
export class UserPage {
  readonly user = input.required<User>();
}

export const routes: Routes = [
  {
    path: 'users/:id',
    component: UserPage,
    resources: (ctx) => ({
      user: userResource(() => ctx.params()['id']),
    }),
  },
];

Ici, user est une ressource bloquante : le routeur attend qu'elle ait une valeur avant d'activer UserPage. Grâce au binding des inputs, le composant reçoit directement un User, et non l'objet Resource. C'est proche de l'expérience d'un resolver, mais le chargement est désormais réactif et peut être coordonné avec les autres ressources de la navigation.

L'exemple utilise fetch pour rendre le mécanisme lisible. Dans une application Angular, un httpResource() permet généralement de conserver les interceptors, les outils de test HTTP et la configuration HttpClient existante.

Afficher la page sans attendre : nonBlocking

Toutes les données ne méritent pas de retarder l'affichage d'une page. Une page produit peut être utile immédiatement, même si les recommandations ou les commentaires arrivent ensuite.

La fonction nonBlocking() indique au routeur qu'il ne doit pas attendre une Resource. Cette fois, le composant reçoit la Resource complète et choisit comment afficher son cycle de vie.

ts
import { Component, input, Resource, resource } from '@angular/core';
import { nonBlocking, Routes } from '@angular/router';

function recommendationsResource() {
  return resource({
    loader: async () => {
      const response = await fetch('/api/recommendations');
      return response.json() as Promise<string[]>;
    },
  });
}

@Component({
  template: `
    @if (recommendations().isLoading()) {
      <p>Chargement des recommandations…</p>
    } @else if (recommendations().hasValue()) {
      <ul>
        @for (item of recommendations().value(); track item) {
          <li>{{ item }}</li>
        }
      </ul>
    } @else {
      <p>Les recommandations sont indisponibles.</p>
    }
  `,
})
export class ProductPage {
  readonly recommendations = input.required<Resource<string[] | undefined>>();
}

export const routes: Routes = [
  {
    path: 'products/:id',
    component: ProductPage,
    resources: () => ({
      recommendations: nonBlocking(recommendationsResource()),
    }),
  },
];

Le choix entre bloquant et non bloquant est un choix d'expérience utilisateur :

  • utilisez une ressource bloquante quand la page ne peut pas être comprise sans la donnée ;
  • utilisez une ressource non bloquante quand un état de chargement local est acceptable ;
  • n'ajoutez pas de blocage par défaut : une navigation rapide avec un squelette ou un message clair est souvent plus agréable.

La Resource est créée dans la fonction resources, via une fabrique. Elle appartient ainsi au cycle de vie de la route qui la crée.

Que deviennent les resolvers ?

Les resolvers restent l'API stable et adaptée aux applications Angular actuelles. Les Router Resources ne les déprécient pas et ne constituent pas une migration obligatoire.

Elles proposent néanmoins une direction intéressante : les données de navigation utilisent le même modèle réactif que le reste de l'application. Le routeur peut gérer ensemble les ressources d'une navigation, tandis que le composant reste centré sur l'affichage. Les ressources bloquantes gardent une ergonomie proche d'un resolver ; les non bloquantes rendent explicites les états de chargement dans le composant.

Une Resource est également rechargeable. La map resources de ActivatedRoute donne accès aux ressources actives de la route, ce qui permet de recharger une donnée sans déclencher toute une nouvelle navigation. Cet usage reste à évaluer avec prudence tant que l'API est en preview.

Faut-il l'essayer maintenant ?

Oui, dans une branche d'expérimentation ou pour un prototype. C'est une occasion utile de repérer les routes dont les chargements sont indépendants, et de réfléchir aux données qui doivent réellement bloquer l'écran.

En revanche, évitez de baser une migration large sur la préversion. Angular précise que les APIs Developer Preview peuvent changer, y compris entre des versions mineures ou correctives. Attendez une API stable et une documentation finalisée avant de l'adopter comme convention d'équipe.

Le point à retenir est moins le nom de l'API que son modèle : déclarer les besoins de données au niveau de la route permet au framework de mieux orchestrer le chargement. Si Angular stabilise cette proposition, les applications pourront bénéficier d'une navigation plus prévisible sans renoncer aux Signals ni à RxJS.

Sources