[DevoxxFR 2014] Au secours, mon code AngularJS est pourri !

06 mai 2014

double_xx_texte

Thierry Chatel, consultant en architecture logicielle, formateur spécialisé sur AngularJS et créateur du blog frangular.com est venu nous présenter un ensemble de recommandations pour avoir du code AngularJS propre !

Il débute la conférence en expliquant qu’Angular offre tous les outils nécessaires pour avoir du code structuré, tout en laissant beaucoup de liberté au développeur. La découverte du langage se fait souvent par l’exemple et on peut se lancer très rapidement dans quelque chose de fonctionnel mais qui peut vite devenir complexe et désordonné. Il est ainsi possible de faire beaucoup de choses de différentes façons et les éléments à disposition ne sont pas toujours utilisés de manière idéale. Thierry fait au passage une petite touche d’humour en faisant référence à Fred Brooks et son ouvrage : “No Silver Bullet”. Angular n’est donc pas magique en soit mais c’est bien des bonnes pratiques qui sont nécessaires pour avoir un code présentable.

Généralités

Il porte ensuite notre attention sur plusieurs points. Le fait que l’utilisation de certains composants (tel que ng-grid) peut entraîner des problèmes de performances et qu’il n’est pas nécessaire de chercher systématiquement à optimiser. Il entend par là que investir du temps pour optimiser les performances n’en vaut pas toujours la peine, dans la mesure où celles-ci seront invisibles en termes d’interface. Si votre code fonctionne et ne comporte pas de problèmes évidents de performances, n’y touchez pas ! Si par exemple, une petite fonction est lancée 3 fois au lieu d’une, l’utilisateur ne sentira pas la différence. Ne gaspillez pas vos ressources.

Il recommande ensuite d’utiliser de vrais objets pour stocker les résultats de ressources extérieures au lieu par exemple de retourner directement du JSON. Enfin, dernier conseil général, si un service est utilisé partout dans une application, il est préférable de le mettre directement dans le scope au lieu de l’injecter.

Organisation des fichiers

L’organisation par type (controller/service/filter…) n’est pas recommandée car dès que l’application atteint une certaine taille, c’est rapidement le bazar et ce n’est absolument pas pertinent. Il faut plutôt essayer de découper d’un point de vue fonctionnel, c’est à dire par fonctionnalité.

organisation.png

Un module doit toujours avoir son propre fichier JS. Ceci est valable également  pour les controllers, services, filters, etc… Dans le cas contraire, l’ordre de chargement des fichiers / modules devient important, ce qui rend les TUs pénibles et n’apporte strictement aucun avantage.

organisation2.png

Il peut être pertinent de mettre les fichiers tests, templates et autres ressources dans les mêmes dossiers contenant le code. Dans le même esprit, il est préférable de séparer les déclarations de routes par fonctionnalités. Enfin, il est aussi intéressant de déclarer un service contant() (utilisable dans config()) pour définir les chemins des templates.

Controllers

Le but des controllers est d’initialiser le scope et c’est tout !

  • Pas de traitement de données

  • Il s’agit juste de faire le lien entre les services et le scope

Il peut aussi être pratique de faire un controller pour une portion de vue, typiquement sur un scope répété (ng-repeat). La syntaxe “controller as” est préconisée par Google. Elle facilite l’héritage entre scopes et est beaucoup plus explicite dans les templates. Le seul inconvénient à cela est que c’est légèrement plus verbeux. Thierry finit par une petite touche d’humour qui résume la partie sur les controllers : “Un controller c’est mignon quand c’est petit.”.

Services

A un service ne doit être associé qu’une seule fonctionnalité. En revanche, celle-ci doit figurer dans son intégralité.

Les services doivent contenir tout le code métier ! A proscrire donc traitements dans les contrôleurs et règles métier dans les templates

Exemple :

  • Conseillé : ng-class=”{alert:orderSrv.isAlert(row)}”

  • A éviter :   ng-class=”{alert: quantity(row) >= 100}”

A noter également que tous les services sont des singletons et qu’ils conservent donc les données globales ainsi que les traitements. Au niveau des services asynchrones, la recommandation est de renvoyer toujours des promesses et jamais directement le résultat. Dans le cas d’un problème lors de l’appel, il est possible de renvoyer une promesse en erreur ou de lever une exception.

Enfin il est recommandé de découper les services en couches, par exemple dans le cas de la gestion des erreurs et notifications on pourrait avoir :

  • un intercepteur $http

  • surchage du service $exceptionHandler

  • un service pour les erreurs

  • un service de log serveur

  • un service de notifications

Filtres

La règle est qu’un filtre ne doit jamais modifier les données qu’il a en entrée. Il doit impérativement recréer de nouvelles données.

Typiquement, lorsqu’on trie un tableau avec des filtres, il ne faut pas modifier les données d’entrées pour pouvoir afficher à nouveau le tableau dans son intégralité si on enlève le filtre.

Directives

Dans la mesure du possible, privilégier les bindings aux manipulations du dom. C’est à dire qu’il faut s’appuyer sur le HTML au lieu de le remplacer (“syndrome JSF !”). L’idée est donc d’enrichir un attribut HTML, ce dernier faisant office d’interface pour la directive.

Si on souhaite passer des données entre des directives parent/enfant, il est nécessaire de faire un contrôleur de directives. Il ne faut surtout pas utiliser d’héritage implicite !

Il est d’ailleurs possible de créer des directives sur les éléments HMTL standards (tels que input, form, script…) ce qui est largement préférable au fait d’aller chercher les éléments dans le DOM.

Tests

A la question de savoir si les tests d’une application Angular doivent être soignés, la réponse est bien évidemment oui. Tout simplement car si on essaie de gagner en rapidité en les écrivant plus vite on perdra très vite en maintenabilité par la suite. Comme dans tous les langages…

Quelques astuces :

  • Imbriquer un describe pour factoriser dans un beforeEach

  • Au niveau de protractor (pour les tests end 2 end), factoriser des ElementFinder et des fonctions.

Conclusion

Ce qu’il faut retenir globalement de cette conférence :

  • Faire au plus simple

  • Découper le plus possible en services

  • Bien connaître javascript et profiter de sa souplesse

  • Partir du HTML

  • Utiliser les bonnes pratiques de POO pour structurer son modèle de données !

    • voir la notion “d’Object Literal” en javascript

Merci à Thierry pour tous ces conseils, vous pouvez le suivre sur @ThierryChatel et on espère que tout cela sera utilisé pour vos futures projets AngularJS !

Les slides sont disponibles ici : http://www.methotic.com/thierry/devoxx-france-2014/slides.pdf

Florent Lagrede (1 Posts)


Maxime Schneider-Dufeutrelle (4 Posts)


Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Vous pouvez utiliser ces balises et attributs HTML : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>