Angular : Les resolvers

En Angular il existe un type de services que je n'ai pas vu dans beaucoup de projets, framework, etc : les Resolvers.

Au départ il est assez compliqué de comprendre l'intérêt d'un resolver qui semble à peine faire quelque chose de plus que le simple service qui discute avec un serveur distant. Il semble même faire moins puisque dans les cas basiques il ne fait qu'appeller d'autres services.

Prenons un exemple pour construire notre reflexion, dans mon application Angular j'ai un composant qui s'occupe d'afficher une liste d'article de blog. Les dits "articles" sont stockés sur un serveur distant. J'utilise donc un service ArticleService qui fait l'appel HTTP et me retourne la liste d'articles.

Résolution des données

Le but du Resolver est de résoudre les données selon les besoins d'un composant / d'une page / d'un écran donné.

Par "résoudre les données" j'entends : construire, adapter, modifier, consolider les données pour que celles-ci puisse être utilisées simplement par le composant qui en a besoin.

Le composant pourrait le faire lui-même (et c'est d'ailleurs ce qu'il fait dans beaucoup de projets où les développeurs n'ont pas connaissance des Resolvers ou de leur utilité.

On rajoute donc ici une nouvelle couche d'abstraction, on sépare un peu mieux les responsabilités. Ce qui est très bien[1]. Le service "web" récupère les données depuis l'API, le Resolver prépare les données, le composant utilise et interagit avec les données.

Si nous reprennons notre exemple, j'ai deux possibilités : sans resolvers, avec resolvers.

Sans les resolvers

L'utilisateur click sur un lien vers notre page. Le composant se charge. Il a besoin des articles, il appelle donc le service qui s'en charge (ArticleService).

Les articles fournit par l'API ne sont pas au bon format pour être utilisés directement par le composant. Par exemple, les articles possèdent tous un statut mais l'API ne fournit que l'ID de statut de chaque article. Le composant ne peut pas afficher uniquement des ID à l'utilisateur et n'a pas la connaissance de la correspondance id => libelle.

Le composant appelle donc le service qui s'occupe des statuts (StatutService) et remplace chaque ID de statut par le statut correspondant.

Une fois les articles formatés correctement il les affiche.

Vous voyez où est le problème de responsabilité ici ? Le composant passe clairement plus de temps à récupérer et adapter les données qu'à les afficher et intéragir avec elles.

Avec les resolvers

L'utilisateur click sur un lien vers notre page. Angular Router lance le resolver (ArticlesResolverService), qui récupère les articles grâce à ArticleService. Il y ajoute les bons statuts grâce à StatutService.

Une fois les données récupérées et re-travaillées, le resolver les transmet à Angular Router qui les ajoute à la route. Il lance le composant et lui injecte la route.

Le composant récupère les données depuis la route comme s'il s'agissait d'un corps de requête ou d'une simple querystring. Le composant affiche les données.

Preloading des données

Autre avantage des Resolvers : ils permettent de preloader les données. Nous l'avons rapidement vu dans la résolution des données mais nous ne nous sommes pas arrêté dessus.

Comme le Resolver prépare les données pour le composant, Angular ne va pas charger le composant tant que les données ne sont pas prêtes. La page ne s'affichera donc pas tant que tous les Resolvers de cette page n'auront pas fini leur boulot.

Reprenons à nouveau notre exemple et le comparatif des comportements.

Sans les resolvers

L'utilisateur click sur un lien menant à notre page. Le composant utilise le service ArticleService dans sa méthode ngOnInit pour récupérer les articles. Grâce au two-way data binding les articles s'affichent automatiquement sur la page web une fois chargée.

La page web apparaît donc d'abord vide ; lorsque le chargement des éléments est terminé ils sont affichés.

Avec les resolvers

L'utilisateur click sur un lien menant à notre page. Angular Router lance le resolver (ArticlesResolverService), celui-ci appelle les différents services dont il a besoin (ArticleService).

Il peut avoir accès aux informations de la route (paramètres, chaine de requête, corps de requête, etc.) pour faire sa résolution même si ici il n'en a pas besoin.

Lorsque la résolution est terminé (juste le chargement des données dans notre cas simple) le résutat est passé au composant. Dans la methode ngOnInit le composant peut récupérer les données comme s'il s'agissait uniquement d'un attribut de la route.

La page apparaît donc uniquement lorsque les données fournie par le résolver sont chargées.

Conclusion

Les resolver sont des services intéressants dans le sens où ils vous permettront de simplifier cognitivement votre code, de respecter S.O.L.I.D. et de profiter du preload des données là où c'est nécessaire.

Bien-sûr, il n'y a rien de réellement spécifique à Angular ici. Certes Angular Router intègre les resolvers dans son mode de fonctionnement (cycle de vie d'une requête, navigation, etc.) mais il est tout à fait possible de reproduire cette methode de développement avec d'autres technologies.


  1. Les principes de S.O.L.I.D. ↩︎