Migrer vers Angular, les pièges à éviter
Il existe pas mal d’articles traitant des points de compatibilités (ou d’incompatibilités) entre AngularJS et Angular mais très peu traitent des pièges liés à la migration. C’est tout l’objet de cet article.
Avant de rentrer dans le vif du sujet, un petit rappel. Angular est devenu aujourd’hui un Framework incontournable et plébiscité par de nombreux développeurs. Sa version actuelle est une rupture majeure avec AngularJS essentiellement basée sur l’écosystème et le langage JavaScript. Nous avons déjà eu l’occasion de nous exprimer sur la première monture du Framework de Google, AngularJS. Nous déconseillons vivement son utilisation dans le cadre d’un projet complexe. Pas de notion native de module, pas d’utilisation possible de méta-données, un support très limité par les IDE du fait du typage faible. De nombreuses raisons qui nous font préférer des Framework Web mieux outillés que ce soit dans le monde Java ou .NET.
Avec l’arrivée à maturité d’Angular, les choses ont changé. La communauté Angular est aujourd’hui confrontée à une situation complexe. Avec la montée en puissance d’Angular, les entreprises ayant opté pour AngularJS doivent inévitablement se poser la question de la migration. Chacun fera ses choix mais il faut savoir qu’Angular apporte indéniablement des bénéfices d’un point de vue de la maintenabilité. Il est probablement moins simple à appréhender qu’AngularJS mais au final on ne le regrette pas. Le développeur découvre en phase de compilation que son code ne fonctionnera pas grâce à l’utilisation de TypeScript et bénéficie de l’approche par composant, une avancée majeure par rapport aux directives orientées HTML d’AngularJS.
Cela étant, est-il facile de migrer son code AngularJS vers Angular lorsqu’on est développeur JavaScript ?
Différences d’architecture
L’architecture Angular est assise sur le concept de composant. Un composant est une balise personnalisée de type « <monComposant></monComposant> » qui vient avec un contrôleur et une vue. Le contrôleur est une classe dont les propriétés membres sont ensuite réutilisées dans la vue via des mécanismes uni ou bi-directionnel de binding, il gère également les traitements associés à la vue (click d’un bouton, sauvegarde, édition,…) en interne ou en s’appuyant sur des services externes injectés automatiquement sur demande. Un composant peut être composite, c’est-à-dire qu’il possède un ou plusieurs éléments fils à qui il transmet des données en entrée et récupère éventuellement des données en sortie. C’est la base d’Angular. Lorsque vous avez l’habitude des directives à la sauce AngularJS, elles disparaissent dans Angular au profit des composants, plus lisibles et maintenables. Leur équivalent, les directives d’attributs deviennent moins indispensables.
Le langage Typescript
La particularité première d’Angular est indéniablement son langage. Qu’on aime ou pas JavaScript, Angular a fait son choix : ce sera un JavaScript typé aux forts accents Java ou C#. Il a fallu des années d’AngularJS pour se rendre compte que JavaScript n’était pas vraiment le langage idéal pour construire des applications métier complexes. Typescript est un langage très élégant avec son initialisation automatique de variables membres via constructeur. TypeScript marie Java et C# (classes, enums, annotation, modules&namespaces, …) tout en s’intégrant parfaitement avec le monde JavaScript. Attention à ne pas pousser le bouchon de l’objet trop loin car certains aficionados de l’objet pourraient être déçus, notamment par l’utilisation des interfaces (et son incompatibilité avec l’opérateur instanceof). Dans tous les cas, ne vous lancez pas dans un projet de migration si vos développeurs n’ont pas été formés à Typescript au préalable. Privilégiez le choix d’une formation dont les premiers chapitres mettent l’accent sur le langage avant de creuser les arcanes d’Angular.
Programmation réactive
Lorsqu’on se plonge pour la première fois dans la documentation officielle d’Angular, on est vite submergé par la quantité d’information et l’hétérogénéité des différents articles proposés. Et pour cause, certains exemples s’appuient parfois sur la programmation réactive pour récupérer le résultat d’une requête, trier ou filtrer des éléments d’une collection, d’autres sur la programmation procédurale. Il faut savoir faire le tri et privilégier l’homogénéité. Auparavant, là où il suffisait de faire console.log($routeParams.param1) pour afficher un simple paramètre d’URL il va falloir désormais écrire : this.activatedRoute.queryParams.subscribe(params => console.log(params[‘param1’]));
Nous n’entrerons pas dans les détails de la programmation réactive, nous avons déjà eu l’occasion d’en parler dans cet article (http://www.dng-consulting.com/?p=892). Sachez simplement que si bon nombre de développeurs savent coder procédural, l’immense majorité est loin d’être familiarisée aux constructions de code réactif. Il faut non seulement savoir utiliser à bon escient les opérateurs map, flatmap, filter & co mais aussi savoir chaîner des appels complexes réactifs. Voici un exemple très simple qui vous donne une idée du fossé existant entre les deux approches. La société Telerik (un éditeur de composants reconnu du marché) propose une boite de dialogue Angular. Imaginons que nous souhaitions simplement afficher une boite de confirmation « Oui » et « Non » et récupérer la réponse de l’utilisateur. Le développeur « procédural » pense instantanément à écrire une API avec une méthode qui va simplement construire la boite de dialogue et renvoyer true ou false : Une sorte de confirm() : boolean.
Lisez attentivement l’API du composant en question permettant de récupérer le bouton sélectionné par l’utilisateur. Vous comprendrez très vite qu’un développeur procédural « lambda » pourrait rester planté quelques minutes devant son écran à imaginer le code à écrire (http://www.telerik.com/kendo-angular-ui/components/dialog/service/#toc-usage). En effet, l’objet DialogRef possède bien une propriété « Result » décrivant l’action de l’utilisateur sur la popup. Mais celle-ci a une drôle de tête :
« Result » est de type Observable<DialogResult>. Voilà comment une simple action de récupération de la valeur d’un bouton oui/non se transforme en :
public confirm(title : string, message : string) : Observable<boolean> { const dialog: DialogRef = this.dialogService.open({ title: title, content: message, actions: [ { text: yesNo.no }, { text: yesNo.yes, primary: true } ] });return dialog.result.map<DialogResult, boolean>(result => { if (result instanceof DialogCloseResult) { return false; } else return ((<DialogAction>result).text === yesNo.yes)?true:false; }); } |
On est loin, très loin du boolean result = confirm(title,message). En programmation réactive, tout est observable, de la simple chaine de caractère d’un bouton aux éléments d’une collection. Toute source de données est potentiellement de type Observable<T>. Il faut le savoir et l’intégrer.
Les bibliothèques de composant
Une fois le langage Typescript maîtrisé et la programmation réactive en tête, il reste à construire l’application. C’est alors que va se poser la question du choix des composants. Les menus, les arbres, les onglets, les fenêtres ou dialogues, les grilles avec possibilité de paginer, filtrer, trier. On pourrait se dire que presque deux ans après la sortie d’Angular, l’écosystème est au point et que la plupart des bibliothèques AngularJS ont été portées en Angular. Que nenni. Prenons Bootstrap. Il existe plusieurs projets opensource existants : ngx Bootstrap (http://valor-software.com/ngx-bootstrap/#/) ou ng-Bootstrap (https://ng-bootstrap.github.io/#/components/accordion) proposé par l’équipe AngularUI. Il ne faut pas plus de quelques minutes pour se rendre compte que la charge de travail pour atteindre le niveau de maturité d’AngularJS UI est encore long, les démos parlent d’elles-même. C’est encore plus le cas pour la grille AngularJS (http://ui-grid.info/) dont l’équivalent Angular 2 est à l’état de … spécification (https://github.com/angular-ui/ui-grid/issues/4294).
Côté Material UI, les choses sont un peu plus avancées car Google est une des principales locomotives de ce design graphique. La bibliothèque « officielle » Material Angular (https://material.angular.io/categories/forms) a une grosse activité depuis le début de l’année (notamment en Avril 2017) mais n’espérez pas faire des prouesses graphiques sur le desktop, les composants sont pour la plupart assez limités et généralement adaptés aux terminaux mobiles. Quant aux grilles, elles sont là encore plutôt inexistantes en OpenSource ou alors très limitées : http://angular-data-grid.github.io/demo/material/.
En résumé, si vous vous lancez dans une migration Angular 2 avec des composants AngularJS existants plutôt riches, nous vous conseillons vivement d’aller voir du côté des éditeurs commerciaux tels que Telerik (http://www.telerik.com/kendo-angular-ui/) ou DevExpress (https://js.devexpress.com/Demos/WidgetsGallery/Demo/DataGrid/ColumnTemplate/Angular/Light/) avec l’inconvénient d’avoir des composants dit « wrapper ». Vous remarquerez que les sociétés possédant un existant JavaScript sont celles qui proposent l’offre la plus riche avec Angular. Un peu comme GWT à l’époque qui s’appuyait sur des composants JavaScript faute de composants Java natifs. La conséquence est la même, en cas de personnalisation nécessaire des composants en question, vous aurez probablement à mettre la main dans du bon vieux JavaScript là où un composant écrit nativement en TypeScript aurait été plus maintenable.
ReactiveForm vs Template Forms
Une fois la bibliothèque de composant retenue, reste à créer un formulaire. Après avoir parcouru les millions de lignes de la documentation d’Angular on s’aperçoit qu’il n’existe pas une mais deux façons de créer des formulaires. L’approche orientée template et l’approche réactive. Ce qui est déroutant c’est que ces deux approches sont mises en avant alors qu’une des deux a clairement les faveurs de Google. Dans la pratique, l’approche Reactive est plus adaptée aux scénarios complexes là où l’approche Template via le databinding se rapproche de celle d’AngularJS. Nous vous recommandons donc sans hésiter les Reactive Forms plus déterministes et surtout synchrones. A remarquer qu’il est tout de même assez cocasse que ce qui a fait la popularité d’AngularJS, le databinding bi-directionnel, devient aujourd’hui un handicap. L’attribut « ngModel » n’est pas une directive de ReactiveFormsModule.
L’approche dynamique vs l’approche statique
Si vous aviez l’habitude d’inclure des fragments de template via la directive ng-include, sachez que cette directive, comme d’autres (voir https://angular.io/guide/ajs-quick-reference) disparait pour des raisons de sécurité (https://github.com/angular/angular/issues/7596). L’exemple de ng-include n’est pas anodin car c’est une directive dont l’équivalent Angular peut vite se transformer en usine à gaz. En effet, non seulement Angular est devenu de plus en plus statique (les composants sont compilés avant la phase de runtime) mais l’implémentation d’écrans ou de composants dynamiques (la force d’AngularJS avec $compile() ou ng-include) devient incompatible avec la nouvelle architecture.
Cet échange sur Stackoverflow aborde un peu plus en détail le sujet, il faudra passer par un ComponentFactoryResolver, loin d’être simple : https://stackoverflow.com/questions/38888008/how-can-i-use-create-dynamic-template-to-compile-dynamic-component-with-angular
Pour toutes les autres directives, Angular fournit sur son site un guide d’équivalence (https://angular.io/guide/ajs-quick-reference).
Les autres sujets
D’autres points de vigilance sont à noter dans la migration, notamment l’encapsulation du DOM via le Shadow DOM qui permet de créer des styles « internes » à un composant afin d’éviter d’éventuels conflits de noms.
C’est aussi le cas de la gestion évènementielle qui permet à un composant de communiquer avec un autre composant via l’EventEmitter, un sujet RxJS. Concrètement, vos développeurs ne pourront pas faire l’impasse sur RxJS utilisé par Angular Core intensivement.
Conclusion
Cet article est loin d’être exhaustif, nous n’avons pas évoqué toute la partie packaging et l’environnement de développement généré par Angular CLI (Command Line Interface). Des outils tels que Webpack, SystemJS ou Gulp utilisés intensivement pour construire une application AngularJS deviennent des éléments encapsulés voire parfois totalement masqués par le CLI d’Angular.
Cet article souhaite surtout insister sur la nécessité de former ses développeurs AngularJS avant de les lancer dans un projet de migration. Ce n’est pas parce qu’on maîtrise AngularJS qu’on sera à l’aise avec Angular qui introduit des concepts totalement nouveaux : le langage typescript, la programmation réactive, les formulaires réactives, l’approche composant, la compilation statique, le CLI et bien d’autres aspects. Mais ces évolutions en valent la peine, une application complexe réalisée avec Angular et Typescript sera indéniablement plus maintenable qu’une application AngularJS.
Sami Jaber
DNG Consulting
Notre formation Typescript
Notre formation Angular