Routeur Angular : Comprendre l’État du Routeur

Philippe Martin
Angular
Published in
6 min readOct 31, 2016

--

Traduction de l’article “Angular Router: Understanding Router Statede Victor Savkin.

Une application Angular 2 est une arborescence de composants. Certains de ces composants sont des composants d’UI réutilisables (par ex. listes, tables), et certains sont des composants d’application, qui représentent des écrans ou des blocs logiques de l’application. Le routeur s’occupe de composants d’application ou, pour être plus précis, de leurs dispositions. Appelons ces dispositions de composants des états de routeur. En d’autres termes, un état de routeur est une disposition de composants d’application qui définit ce qui est visible à l’écran.

Cet article est basé sur le livre « Angular 2 Router » que vous pouvez trouver ici https://leanpub.com/router. Le livre va au-delà d’un simple guide de démarrage et décrit le routeur en profondeur. Le modèle mental, les contraintes de design et les subtilités de l’API — tout est couvert. Si vous aimez l’article, consultez le livre !

RouterState et RouterStateSnapshot

Lors d’une navigation, après avoir appliqué les redirections, le routeur crée un RouterStateSnapshot. Qu’est-ce qu’un RouterStateSnapshot, et en quoi est-il différent d’un RouterState?

RouteStateSnapshot est une structure de données immuable représentant l’état du routeur à un moment particulier dans le temps. Chaque fois qu’un composant est ajouté ou supprimé ou qu’un paramètre est mis à jour, un nouvel instantané est créé.

RouterState est similaire à RouteStateSnapshot, à la différence qu’il représente l’état du routeur changeant au fil du temps.

RouterStateSnapshot

Comme vous pouvez le voir RouterStateSnapshot est une arborescence d’instantanés de routes activées. Chaque nœud dans cette arborescence connaît les segments d’URL « consommés », les paramètres extraits et les données résolues. Pour être plus clair, regardons l’exemple suivant :

Lorsque nous naviguons vers “/inbox/33/messages/44”, le routeur va examiner l’URL et construire le RouterStateSnapshot suivant :

Après cela le routeur va instancier ConversationCmp avec MessageCmp à l’intérieur.

Imaginons maintenant que nous naviguons vers une URL différente : “/inbox/33/messages/45”, ce qui créera l’instantané suivant :

Pour empêcher des modifications du DOM inutiles, le routeur va réutiliser les composants lorsque les paramètres des routes correspondantes changent. Dans cet exemple, le paramètre id du composant message a changé de 44 en 45. Ceci veut dire que nous ne pouvons pas simplement injecter un ActivatedRouteSnapshot dans MessageCmp car l’instantané ayant toujours le paramètre id mis à 44, il ne sera plus à jour.

L’instantané de l’état du routeur représente l’état de l’application à un moment donné dans le temps, d’où le nom d’instantané. Mais les composants peuvent rester en vie pendant des heures, alors que les données qu’ils affichent peuvent changer. Ainsi, avoir uniquement des instantanés ne fonctionne pas — nous avons besoin d’une structure de données nous permettant de gérer les changements.

Introduction du RouterState !

RouterState et ActivatedRoute sont similaires à leurs contreparties instantanées à la différence qu’ils exposent toutes leurs valeurs sous forme d’observables, qui sont parfaits pour traiter des valeurs changeant dans le temps.

Tout composant instancié par le routeur peut injecter son ActivatedRoute.

Si nous naviguons de “/inbox/33/messages/44” vers“/inbox/33/messages/45”, l’observable de données va émettre un nouveau jeu de données avec le nouvel objet ‘message’, et le composant va afficher Message 45.

Accéder aux Instantanés

Le routeur expose les paramètres et données sous forme d’observables, ce qui est pratique la plupart du temps, mais pas toujours. Parfois, ce que nous voulons est l’instantané de l’état que nous pouvons examiner immédiatement.

ActivatedRoute

ActivatedRoute fournit un accès aux observables url, params, data, queryParams et fragments. Nous allons les voir un par un en détails, mais examinons d’abord les relations entre eux.

Les changements d’URL sont la source de tout changement dans une route. Et cela doit en être ainsi car l’utilisateur a la possibilité de modifier la location directement.

Chaque fois que l’URL change, le routeur en dérive un nouveau jeu de paramètres : le routeur prend les paramètres positionnels (par ex ‘:id’) des segments d’URLs mis en correspondance et les paramètres matriciels du dernier segment d’URL mis en correspondance et les combine. L’opération est pure : l’URL doit changer pour que les paramètres changent. Ou dit autrement, une même URL donnera toujours un même jeu de paramètres.

Ensuite, le routeur invoque les résolveurs de données de la route et combine les résultats avec les données statiques fournies. Les résolveurs de données étant des fonctions arbitraires, le routeur ne peut pas garantir que vous obtiendrez toujours le même objet à partir d’une même URL. La plupart du temps, même, ce n’est pas le cas ! L’URL contient l’id d’une ressource, qui est fixe, et le résolveur de données récupère le contenu de cette ressource, qui varie en général avec le temps.

Pour finir, la route activée fournit les observables queryParams et fragment. À l’inverse des autres observables qui sont attachés à une route particulière, les paramètres de requête et le fragment sont partagés entre plusieurs routes.

URL

Étant donné ceci :

En naviguant d’abord vers “/inbox/33/messages/44” puis vers “/inbox/33/messages/45”, nous allons voir :

url [{path: ‘messages’, params: {}}, {path: ‘44’, params: {}}]
url [{path: ‘messages’, params: {}}, {path: ‘45’, params: {}}]

Nous n’écoutons en général pas les changements d’URL car ceux-ci sont de trop bas-niveau. Un cas où cela peut être pratique est lorsqu’un composant est activé par une route joker. Dans ce cas, le tableau de segments d’URL n’étant pas fixe, il peut être utile de l’examiner pour montrer des données adaptées à l’utilisateur.

Params

Étant donné ceci :

En naviguant d’abord vers “/inbox/33/messages;a=1/44;b=1” puis vers“/inbox/33/messages;a=2/45;b=2”, nous allons voir :

params {id: ‘44’, b: ‘1’}
params {id: ‘45’, b: ‘2’}

La première chose à noter est que le paramètre id est une chaîne (avec des URLs nous travaillons toujours avec des chaînes). La seconde chose est que la route récupère uniquement les paramètres matriciels de son dernier segment d’URL. Raison pour laquelle le paramètre ‘a’ n’est pas présent.

Data

Prenons la configuration ci-dessous pour voir comment fonctionne l’observable ‘data’.

Où MessageResolver est défini ainsi :

La propriété data est utilisée pour passer un objet fixe à une route activée. Elle ne change jamais tout au long de la vie de l’application. La propriété ‘resolve’ est utilisée pour les données dynamiques.

Notez que dans la configuration ci-dessus la ligne “message: MessageResolver” ne dit pas au routeur d’instancier le résolveur. Il demande au routeur d’en récupérer un par injection de dépendance. Ceci implique que vous devez enregistrer “MessageResolver” dans une liste de fournisseurs quelque part.

Une fois que le routeur a récupéré le résolveur, il va en appeler la méthode ‘resolve’. La méthode peut retourner une promesse, un observable ou tout autre objet. Si la valeur retournée est une promesse ou un observable, le routeur va attendre que la promesse ou l’observable se termine avant de passer à l’activation.

Le résolveur n’est pas forcément une classe implémentant l’interface `Resolve`. Il peut aussi être une fonction :

Le routeur combine les données résolues et statiques dans une propriété unique, à laquelle vous pouvez accéder ainsi :

En naviguant d’abord vers “/inbox/33/message/44” puis vers “/inbox/33/messages/45”, nous allons voir :

data {allowReplyAll: true, message: {id: 44, title: ‘Rx Rocks’, …}}
data {allowReplyAll: true, message: {id: 45, title: ‘Angular Rocks’, …}}

Query Params et Fragment

À l’inverse des autres observables qui sont attachés à une route particulière, les paramètres de requête et le fragment sont partagés entre plusieurs routes.

Suivez @victorsavkin pour en apprendre plus sur Angular et TypeScript.

--

--