Des sous-vues dans Angular

Angular, dans sa version actuelle, est très simpliste en ce qui concerne le routage. Il se contente de lier à des routes un template et un controller unique. C'est à dire qu'une application ne pourra contenir qu'une seule et unique vue dynamique gérée par le routage. C'est assez embêtant quand on a des interfaces hiérarchisées comme par exemple :

  • / : affiche une sidebar et un panel
  • /truc : ne change pas la sidebar mais affiche deux colonnes dans le panel
  • /truc/bidule : ne change ni la sidebar ni la dernière colonne du panel mais change la vue centrale
  • /chose : change le panel par une toute autre vue en une seule colonne

Avec angular, on est obligé d'inclure plusieurs fois ces sous-vues dans la vue liée à chaque route et mécaniquement, elles sont toutes rechargées, ce qui peut poser des problèmes de clignotement de l'écran, de réinitialisation des valeurs du $scope par défaut ou de chargement ajax inutiles.

Heureusement, pour palier à cette lacune, il existe UI-Router !

UI Router

UI Router est un module tiers qui permet d'utiliser de multiples sous vues et de composer des routes bien plus complexes que ne le fait Angular par défaut. On peut ainsi hiérarchiser des listes de sous vues et même définir plusieurs sous-vues dynamiques, le tout en ne rechargeant que les sous-vues nécessaires. Mais le mieux est de partir sur un exemple concret.

Exemple

Nous allons partir sur une interface en 3 colonnes : la première à gauche est une navigation principale qui ne change jamais quelle que soit la route. La colonne à droite change selon la route, le panel central aussi. Et le panel central affiche un paramètre inclus dans la route et fourni par un lien de la colonne de droite.

Avec angular de base, on peut s'en sortir en faisant des ngInclude. Le gros problème de ngInclude, c'est qu'on doit inclure le controlleur dans la vue à l'aide de ngController ce qui empêche de réutiliser la dite vue dans un autre contexte et de réellement séparer vue et controller. L'autre problème c'est que nous ne pouvons spécifier dans notre routage qu'une seule vue dynamique. Nous aurons alors dans index.html la vue nav.html et un div avec ngView. Et chaque route ira charger une vue qui elle même aura deux ngInclude. Là où ça devient embêtant c'est que les sous-vues vont appeller la même vue principale en rajoutant juste des paramètres de route et vont donc la recharger inutilement.

Voici ce qu'on peut faire avec ui-router :

States

On définit ici des états au lieu de routes. Cela à plusieurs avantage :

Hiérarchie

On peut hiérarchiser chaque état. Ainsi, nous allons pouvoir définir un état main qui sera utilisé par chaque état. Il est abstrait et n'est même pas accessible via une URL.

Ensuite, un état peut être fils d'un autre et ainsi ne recharger que la sous vue qui lui est consacrée. C'est ce qu'on voit dans l'exemple avec l'état main.trucs.tool.

Plusieurs vues

On peut définir plusieurs vues par état là où Angular se limite à une seule et unique dans toute l'application. Il suffit pour cela de définir l'objet views et de nommer la directive uiView dans le template.

Données figées

On peut figer des données pour une route donnée. Cela peut être pratique quand on veut savoir exactement sur quelle route on est sans pour autant avoir à lire la route complète et la parser. On utiliser alors le paramètre data dans lequel on y mettre tout ce qu'on veut, puis on y accèdera dans le controlleur à l'aide de $state.current.data.

Liens dynamiques

La directive uiSref permet de générer des liens sans avoir à connaitre la route. Au lieu de donner au lien la route complète, on lui donne via uiSref le nom de l'état vers lequel il doit mener ainsi que les éventuels paramètres qu'il doit prendre en compte. L'url sera générée selon la configuration de l'état. Cela sera très pratique en cas de changement d'url : il suffira de le changer à un seul endroit, la config des état.

Clarté de code

Le code est clairement plus clair et séparé. Dans l'exemple joint, on a quasiement pas de code javascript et chaque vue est bien séparée et indépendante. C'est plus difficile sans ui-router dès que l'application devient complexe.

Il y a encore des tas de choses à dire à propos de ce module qui devrait être inclus de base dans AngularJS. Si j'ai un conseil à vous donner, c'est bien de ne pas commencer un projet sans ui-router, vous le regretterez sinon.

Hadrien

Hi, I'm a french Javascript Lead Developer, Web Architect from Toulouse, France. I've worked for 12 years for many projects with YUI, AngularJS, Aurelia.io and now React and React native.

Toulouse, France https://hadrien.eu