AngularJS : navigation et routage

Principe déjà bien connu dans les frameworks web de tous type, la notion de routage permet d’associer une URL à une ressource (page web dynamique ou statique). En restant sur la même ligne, AngularJS propose une construction d’URL permettant de pointer à un endroit précis de son application : le « deep-linking ». La directive ng-view permet de charger des vues, chaque vue étant associée à une URL. La page principale devient alors le template principal et charge, au niveau de la directive ng-view, la vue correspondant à l’URL.

Déclaration des routes

La déclaration des routes se fait via les services $routeProvider et $route en utilisant la méthode config() du module :

2 routes sont ici déclarées pointant chacune vers une vue : une pour afficher une liste d’amis (/list), une autre pour éditer/créer un ami (/edit).

Il n’est pas encore possible de définir plusieurs directives ng-view dans une même page, mais le débat est ouvert. En attendant la directive ng-include permet de palier ce manque et peut être déclarée plusieurs fois dans une même page, reste par contre à gérer soi-même la dynamique d’affichage.

Le service $location

La construction d’URL est compatible avec la recommandation HTML5 History API via l’objet $location. L’idée est qu’au travers de cet objet, un code unique permet de s’adapter suivant si l’on cible des navigateurs compatibles ou pas avec cette API. Par défaut le mode HTML5 n’est pas activé, la méthode html5Mode(true) du service $locationProvider permet de le configurer. Pour avoir plus d’informations sur les 2 modes possibles, suivez le guide.
Dans l’exemple ci-dessus, $location est utilisé pour rediriger l’utilisateur vers la liste d’amis après avoir cliqué sur « Enregistrer » (voir controllers.js) :

Paramètres

Des paramètres peuvent aussi être ajoutés aux URLs. Toujours dans l’exemple ci-dessus, le lien « Editer » fait référence à l’index du tableau en paramètre de l’URL.

et la route est déclarée telle que :

Le service $routeParams vient alors s’ajouter pour donner accès aux paramètres de l’URL.
Soit pour l’URL http://server.com/index.html#/Chapter/1/Section/2?search=moby
et la route: /Chapter/:chapterId/Section/:sectionId
$routeParams renverra {chapterId:1, sectionId:2, search:’moby’}
Dans notre exemple $routeParams est injecté dans le controller FriendEditCtrl

Initialisation et temporisation avant l’affichage d’une page

La méthode de configuration d’une route $routeProvider.when(path, route) décrit la route selon un chemin (ex: /edit) auquel est associé un controleur et un template. Une route peut aussi être un redirection vers une autre URL (redirectTo). Mais il est aussi possible de paramétrer des actions à réaliser avant le chargement de la page via le paramètre resolve. Ainsi si une page doit charger une liste importante de données, ce système permet de faire patienter l’utilisateur avant l’affichage de la page. L’exemple ci-dessous est le même que le précédent à la différence que le tableau d’amis met 2s à arriver.
Pour simuler cela j’utilise dans cet exemple le service $q qui est une implémentation du modèle promise/deferred inspiré par le projet Q de Kris Kowal combiné avec le service $timeout pour déclencher la résolution de l’objet déferré 2s plus tard.
La spécificité du paramètre resolve est qu’il attend une map où chaque clé est considérée comme une référence à un service qui peut donc être injectée dans le controleur et que la valeur contient le service correspondant. Si la valeur retourne un objet « promise » alors celui-ci est résolu avant que le controleur soit instancié et émet l’évènement $routeChangeSuccess après la résolution de l’objet déférré.
=> appuyez sur le bouton play pour voir le comportement : une message « Loading friends … » apparaît pendant 2s puis la liste d’amis s’affiche

Evènement

Notez dans cet exemple l’utilisation des évènements $routeChangeStart et $routeChangeSuccess au niveau de la directive butterBar.

Celles-ci permettent d’intéragir dans le cycle d’affichage des vues et ici afficher un message en attendant l’affichage de la page.

AngularJS : les filtres

Le filtre est une fonctionnalité bien pratique qui va faciliter l’affichage de données. Pour commencer il y a les filtres de formatages qui vont permettre d’afficher une date ou un nombre selon un pattern donné. Les filtres utilisent la syntaxe | (« pipe ») bien connue de tous ceux qui utilisent des commandes shell :

Ou encore simplement des transformations de chaînes de caractères :

Et ll est possible d’enchainer les filtres.

Mais un filtre permet aussi de modifier le DOM ainsi le filtre orderBy facilite le tri de tableau en le combinant à la directive ng-repeat. Notez qu’il est possible de passer des paramètres tel que pour orderBy 2 paramètres sont proposés permettant de passer un prédicat et le sens de tri. Le 2-way-databinding permet d’activer le filtre dynamiquement.

Autre exemple intéressant avec le filtre filter permettant de réduire un tableau de donnée en s’appuyant sur un critère. Ce qui, avec le 2-way data binding, offre un affichage dynamique bien pratique pour améliorer l’expérience utilisateur :

Bien sûr vous pouvez créer vos propres filtres. La syntaxe est similaire aux directives, controleurs et services.
Dans l’exemple suivant le filtre permet de capitaliser une chaîne de caractères.

Cet outil puissant est encore un élèment de plus en terme de lisibilité. Simple à mettre en oeuvre il donne la possibilité d’imaginer des implémentations complexes. Ci-dessous un exemple d’enchainement de 2 filtres sur une répétition pour paginer un tableau :

AngularJS : tests unitaires et d’interface utilisateur

Si il y a bien un élément différenciateur par rapport aux autres framework JS c’est la capacité d’AngularJS à mettre en oeuvre les tests. C’est normal dirais-je puisque le framework a été développé en intégrant dès le départ cet aspect fondamental. JavaScript est un langage dynamiquement typé ce qui le rend simple à écrire mais qui permet aussi d’écrire des erreurs que le compilateur n’indiquera pas. Il est donc d’autant plus important lorsqu’on utilise ce langage de bien le tester.
Mais AngularJS n’est pas magique non plus, comme tous ceux qui ont mis en place des tests le savent, l’important est de penser son code pour les tests. La où il faudra être vigilant est de ne pas faire appel à des objets ou des méthodes sans savoir si ils seront facilement testable. Prenons l’exemple d’un controleur, si au sein de celui-ci des appels sont fait au DOM via le $ de jQuery par exemple, ce code deviendra tout de suite complexe à tester.
Les outils actuels malheureusement ne vont pas nous permettre comme en Java de faire facilement du TDD, où en écrivant mon test l’IDE va proposer la création des classes et des méthodes. Rien n’empêche cependant de le faire.
En théorie AngularJS n’impose pas de framework de test et vous pouvez tester avec vos outils mais l’équipe AngularJS a développé des bibliothèques pour faciliter l’écriture de tests et ceux-ci se basent sur Jasmine et JsTestDriver (dont Misko est co-auteur) :

  • angular-mocks : qui regroupe un ensemble de mock de service de base : $log, $httpBackend, $timeout, $exceptionHandler et aussi des méthodes pour faciliter l’écriture de tests : module (pour charger un module), inject (pour injecter des dépendances)
  • angular-scenario : une extension de Jasmine pour permettre des tests d’interface utilisateur (end to end testing), l’idée est de fournir un DSL facilitant l’écriture de ce type de test.

Passons à la pratique.

Exécution manuelle Jasmine+JsTestDriver

Avec angular-seed des scripts sont fournis pour mettre en place les tests. Il suffit de lancer scripts/test-server.[sh|bat] pour démarrer JsTestDriver et d’ouvrir une page sur chaque navigateur que vous voulez tester à l’adresse http://localhost:9876/. Puis de lancer les tests manuellement avec scripts/test.[sh|bat]. Tous les tests déclarés dans le répertoire test/unit seront exécutés. Vous pouvez modifier ce comportement en ciblant certains test ou d’autres répertoires via le fichier de configuration config/jsTesDriver.conf

Exécution dans un IDE : Eclipse et IntelliJ/WebStorm

JsTestDriver fourni des plugins pour Eclipse et IntelliJ. Vous pouvez donc exécuter vos tests dans votre IDE préféré plutôt qu’en ligne de commande. Attention cependant si vous utilisez angular-seed à revoir les chemins déclarés dans config/jsTesDriver.conf qui ne correspondent pas à une exécution dans un IDE.

Test d’un controleur

Partons du code suivant : une simple liste de nom accompagné d’un bouton de suppression et suivi d’un champ texte avec un bouton pour ajouter de nouveau nom :

Nous devons tester le controleur pour s’assurer que l’appel de la méthode addPatient() ajoute bien un patient dans la liste. Notez l’utilisation de module(‘myApp’) qui va charger le module correspondant (un air de Guice) ainsi que l’utilisation de inject($rootScope, $controller). inject(…) autorise autant d’argument que vous voulez, ces arguments doivent faire référence à des objets connus du module et sont identifiés par leur nom : inject(monService) ne marchera pas si vous n’avez pas déclaré de service monService. De même inject($controleur) ne marchera pas car le service s’appelle $controller.

Test d’une directive

Reprenons ici la directive ui-date de l’article précédent et détaillons la. De même que ci-dessus module() et inject() sont utilisés pour injecter nos dépendances. Notons par contre que l’implémentation du datepicker jQuery-UI est pensée pour les tests car elle permet d’interagir sur le composant via des méthodes qui simulent les actions utilisateur. C’est un point important pour le choix d’une bibliothèque externe que l’on veut encapsuler dans une directive.

Test d’interface utilisateur ou end to end testing

Avec le E2E nous allons pouvoir simuler des interactions utilisateurs avec l’interface graphique. Alors vous me direz « et Selenium alors ? », eh bien certains ont essayés
mais c’est tout de même plus complexe. De plus Selenium ne connait pas le fonctionnement d’AngularJS et les essais menés ont montrés des problèmes de lenteurs. Misko résume dans le forum pourquoi ils n’ont pas utilisé Selenium.
D’autre part l’utilisation des selecteurs jQuery pour cibler les balises est très pratique et simple à écrire.
L’exemple ci-dessous montre comment, à partir du code précédent, tester l’ajout d’un nom dans la liste.

Pour exécuter ce test dans angular-seed il est nécessaire de l’écrire dans test/e2e/scenario.js. Ensuite il faut que votre site soit démarré et il suffit alors d’appeler l’URL http://localhost:port/test/e2e/runner.html dans votre navigateur. Ce qui donne le rendu suivant :

Simulation d’un appel AJAX

Pour pouvoir tester du code faisant des appels serveur, AngularJS fourni un mock du service $http (que nous verrons plus en détail dans un prochain article). Ainsi les tests peuvent être exécuté indépendamment du serveur. Reprenons notre exemple précédent et faisons appel à la resource data/patients.json pour récupérer les noms des patients :

où data/patients.json renvoi

Pour réaliser le test il

est nécessaire d’injecter l’objet $httpBackend. Celui-ci va alors intercepter les appels du service $http pour renvoyer des données de test. Dans le test suivant la méthode when() va indiquer ce que doit renvoyer la resource data/patients.json sera :

$httpBackend fourni 2 méthodes :

  • when() : qui va définir de manière globale ce que renvoi une resource, il n’est pas obligatoire de préciser la resource
  • expect() : qui va définir de manière manière précise ce que renvoi une resource en particulier

A noter aussi la méthode flush(), les requêtes serveur étant asynchrone ceci ne doit pas être le cas dans l’execution des tests. La méthode flush() force donc la requête à s’exécuter et à simuler un mode synchrone.

Testacular

Pour exécuter les tests en temps réel (genre infinitest) et sur différents navigateurs Vojta à développé Testacular : un moteur d’exécution sous node.js intégrable dans IntelliJ/WebStorm. La trace des logs permet de voire la pile d’execution et la référence à son test. IntelliJ/WebStorm rend cela cliquable et vous permet d’aller directement dans le fichier concerné.
Je vous laisse découvrir cet outil pratique à travers cette video.

AngularJS : développement de projet et intégration continue

Pour bien démarrer un projet avec AngularJS vous trouverez sur GitHub : angular-seed, un squelette de projet bien pratique. Une bonne approche est de créer un fichier js pour chaque type d’objet : directives, services, filters, controllers. Dans chacun de ces fichiers les objets sont déclarés au sein d’un ou plusieurs modules comme je le précisais dans mon article précédent.

Pour mettre en oeuvre des tests il faut pouvoir les exécuter facilement. Angular-seed propose des scripts à lancer manuellement. Les tests sont à créer dans le répertoire test sous forme de fichiers js. Le répertoire config permet de définir le contexte des tests et notamment les dépendances. En l’état c’est intégrable dans Jenkins, la team AngularJS l’utilise d’ailleurs comme le montre cette vidéo.

Pour les dépendances c’est pas encore ça. Bien qu’il existe un plugin maven pour gérer des dépendances JavaScript, il ne convient que dans un contexte de développement purement JavaScript. A l’éxecution dans une page HTML il faut reprendre ses dépendances dans les balises <script>.

A l’inverse require.js permet de charger dynamiquement des dépendances JavaScript, et je vous invite à vous inspirer du projet AngularFun pour voir une implémentation avec AngularJS, mais ne s’appuie pas sur un repository et les libs JavaScript doivent être récupérées et déclarées manuellement.

Bref c’est pas encore aussi simple qu’en Java dans Eclipse ou Intellij avec le plugin maven qui vous gère vos dépendances et démarre vos applications. Il y a là plugin à développer pour améliorer nos IDE aux développements JavaScript/HTML/CSS. Les morceaux sont la ils faut les assembler 😉

Vous retrouverez sur le blog de Romain Linsolas une mise en oeuvre de maven avec JavaScript intégrant la gestion des tests avec Jasmine.

Enfin pour ceux qui utilise node.js et le framework web Express, une version d’angular-seed est disponible : angular-express.

J’attends avec impatience la sortie de yeoman.io dont l’objectif est justement de permettre l’intégration continue d’un projet HTML/CSS/JavaScript. yeoman.io ne réinvente pas la roue et s’appuie sur des outils existants (GruntTwitter BootstrapPhantomJSJasmineNodeHTML5 BoilerplateMochaCompass) en les combinant entre eux.

J’ai entendu dire que chez FullSix ils auraient mis en place une intégration continue avec maven pour leur projet JS mais je laisse mon acolyte blogueur vous présenter ça …

AngularJS : modules et services

La notion de module dans AngularJS n’a rien à voir avec celle de ECMAScript prévue pour la prochaine version. Le concept se rapproche plus de celui des modules de Guice pour permettre de faciliter les tests en ne chargeant que certains modules pour son test. C’est aussi une manière élégante d’organiser son code pour regrouper certaines classes.

Un module permet de déclarer :

Il permet aussi de déclarer un code d’initialisation de l’application via la méthode run et de configurer les routes (les URLs pointant vers les vues) avec la méthode config:

La directive ngApp initialise automatiquement l’application. L’affectation d’une valeur contenant un identifiant implique le chargement du module déclaré avec cet identifiant :

Le [ ] liste les modules dépendants

Les services

Ce qui dans nos applications Java est devenu une evidence pour mieux répartir la logique applicative se retrouve aussi dans AngularJS. Comme son nom l’indique le service permet de déclarer une classe dédiée à une fonction précise. Attention chaque service est, et ne peut qu’être (du moins pour l’instant), un singleton.
Dans l’exemple suivant le service MessageService a pour fonction de gérer une liste de message : ajouter, lister, afficher dans une alerte. $window est injecté dans notre service qui est lui-même injecté dans le controleur. Notez la syntaxe, bien qu’il y ait d’autres façons c’est celle qui est la plus lisible. Les injections se font en passant un tableau dont le dernier élement est la fonction avec autant de paramètres que d’éléments précédents dans le tableau tel que :

Vous pouvez toutefois ne pas utiliser le tableau et mettre directement la fonction en nommant les paramètres à l’identique des noms des dépendances (l’ordre n’a pas d’importance). Mais cela à un inconvénient, si vous obfusquer votre code cela ne fonctionnera plus.

Saisissez un message et cliquer sur « ajouter » pour ajouter un message au service. Répétez l’action plusieurs fois puis charger les messages. Ceux-ci s’afficheront en liste et pour chaque un message un bouton « afficher » ouvre une alert() avec le message comme contenu :

Même si cette syntaxe reste peu classique elle a pour avantage de présenter clairement l’injection de dépendances. Encore une fois c’est cette approche qui rend le tout facilement testable.
La tentation d’accumuler du code dans le controleur est forte, le service est la pour bien séparer les fonctionnalités.

Ici nous utilisons la méthode factory(name, fn) pour créer notre service, mais il existe aussi la méthode service(name, fn). La méthode factory prend en paramétre une fonction dont les parametres sont injectés et cette fonction retourne une instance de l’objet service :

La méthode service elle va instancier l’objet service en utilisant le constructeur fourni, simplement comme si il avait été appelé avec l’opérateur new, mais le constructeur est injecté avec ses dépendances

La syntaxe suivante est aussi possible :

La principale différence entre les 2 est que factory() renvoi l’objet service alors que service() fait un new sur l’ojet service et renvoi « this ».

Après avoir fait le tour des principaux concepts, nous verrons dans le prochain article comment construire une application AngularJS avant d’aborder les tests.

AngularJS : directives et composants

La notion de directive est la fonctionnalité la plus intéressante de ce framework. C’est aussi celle qui se rapproche le plus de nos framework web en Java : la création de composants réutilisables.

Une directive permet de (re)définir des balises HTML et ainsi étendre le langage jusqu’à lui donner les propriétés d’un DSL (Domain Specific language). AngularJS fourni un ensemble de directives de base pour concevoir une application web.

La manière de déclarer une directive peut varier pour permettre de s’adapter aux règles d’écriture du HTML (XHTML, XHTML strict, etc). Du coté déclaration en JavaScript la syntaxe utilisée est la même qu’en Java (CamelCase) : maBalise qui se déclinera du coté du code HTML en 5 syntaxes possibles ma:balise, ma_balise, ma-balise, x-ma-balise, data-ma-balise. L’appel pourra être fait en tant que balise, attribut ou classe CSS :




Ci-dessous un exemple d’utilisation des directives de base ng-repeat et ng-include, dont le nom indique bien la fonction. A partir de 2 templates, ici définis via la balise script mais qui aurait tout aussi bien pu être des fichiers HTML, on parcourt un tableau qui affiche ces templates et embarque des paramètres. Notez que les paramètres sont embarqués par l’héritage des propriétés du scope (voir l’article précédent).

  • La directive ng-include transforme un bout de code HTML en un composant réutilisable.
  • AngularJS permet de composer les directives entre elles et de les imbriquer.

Le plus amusant reste la création de ses propres directives pour inventer ses propres balises. C’est vraiment l’originalité de ce framework et qui de plus rend un code lisible et bien structuré.
Commençons simplement par améliorer un input en lui donnant la capacité d’autocompletion en utilisant l’autocomplete de jQuery-ui.

Pour appliquer la directive AngularJS utilise un « compilateur« . Celui-ci parcourt l’arbre DOM au démarrage. Dès qu’il détecte une directive il l’associe au noeud. Une fois toutes les directives détectées pour un noeud, la méthode compile() de chaque directive est exécutée selon sa priorité. La méthode compile() retourne une fonction link() dont l’objectif est de définir les interactions évènementielles avec l’élément et de faire le lien avec le scope (donc le modèle) via la méthode $watch.

Allons plus loin maintenant en définissant notre propre balise. Partons d’un besoin issu de notre application de gestion du dossier patient à l’hôpital : avoir un composant de recherche de patient dans un service qui permettrait aussi d’aller chercher un patient dans les autres services. L’excellent composant select2 améliore la balise select de base en lui ajoutant un champ de recherche et correspond donc parfaitement. Avec la directive on va maintenant pouvoir créer une balise dédiée. Appelons la finder et donnons lui 3 attributs :

  • data : pour passer le tableau d’objets
  • selection : pour définir la variable qui va récupérer l’objet sélectionné
  • favorite : pour indiquer le critère différenciant les patients du service des autres

L’objectif est que les patients du service s’affichent dans la liste déroulante et que les autres ne soient accessibles que par le champ de recherche :

Cette fois-ci des paramètres apparaissent, faisons une brève revue :

  • restrict : pour préciser si la déclaration est l’élément (E), l’attribut (A), la class (C), le commentaire (M)
  • template : remplace la balise par le contenu renseigné
  • replace : pour dire si l’on remplace la déclaration ou pas
  • scope : pour créer un scope fils dédié à notre directive, permet de récupérer les propriétés du scope parent en utilisant ‘=’, de récupérer un attribut en utilisant ‘@’, de récupérer une fonction en utilisant ‘&’.
  • link : le code pour affecter le select2 à l’input et déclarer les évènements qui vont alimenter le modèle

d’autres paramètres existent et pour aller dans le détail je vous invite à consulter la doc.

Le code est simple, bien organisé et reste lisible. Tout est testable (on y reviendra dans un prochain article). Cette méthode de développement permet réellement d’envisager des « big app » en JavaScript.

D’autres exemples qui sont aussi formateurs :

Ainsi que les très bons screencasts de John Lindquist (évangéliste JetBrains et quand on sait qu’ils sont en train de bosser sur un IDE online …)

AngularJS : le scope

Pour faire le lien entre modèle, vue et controleur AngularJS fourni l’objet scope. La notion de scope permet de transporter le modèle entre la vue et le contrôleur. Chaque composant va gérer son propre scope en héritant du scope du composant parent ou du scope racine ($rootScope). Chaque scope peut voir les propriétés du parent.

Le lien avec la vue se fait via l’arbre DOM, un scope est toujours rattaché à un noeud de l’arbre DOM. Le scope racine lui est rattaché au noeud déclarant ng-app, l’attribut d’initialisation d’une application AngularJS. La plupart du temps l’attribut ng-app est déclaré dans la balise html mais il peut être déclarée n’importe où. Pour faciliter le debuggage il est possible d’accéder au scope via l’arbre DOM en appelant angular.element(aDOMElement).scope().

Le scope permet aussi d’interagir avec le data-binding : la méthode $watch(property, function) intercepte les modifications sur la propriété « property » du modèle et de déclenche la fonction « function« . La fonction $apply() permet de dispatcher ensuite la modification sur la vue.

Certaines directives entrainent la création d’un scope fils comme par exemple ng-controller ou ng-repeat. Ce n’est pas une obligation dans la création des directives. L’idée est de pouvoir isoler le comportement d’un composant dans certains cas.
Un bon exemple montrant le fonctionnement du scope est celui de la documentation, repris ci-dessous. Il utilise la fonctionnalité d’évènement du scope. Elle permet d’émettre un évènement soit vers les scopes parents : $emit, soit vers les scopes fils : $broadcast en appelant la fonction $on. L’exemple émet un évènement déclenchant un compteur, le compteur varie en fonction du scope ciblé.

La notion de scope est importante pour introduire les directives dans le prochain article.

AngularJS : less code more fun

L’idée majeure derrière AngularJS c’est le constat qu’HTML n’a pas été conçu pour développer des applications. Pour pallier ce manque le framework propose d’étendre HTML pour y insérer ses propres balises et comportements et d’y intégrer la notion de vues dynamiques. A la différence des autres frameworks la notion de template n’existe pas car le template c’est le HTML, le HTML c’est la vue.

Le projet est né chez Google et conçu par Misko Hevery, Igor Minar et Vojta Jina. Tous trois développeurs Java à la base. Misko étant un des lead des tests chez Google, les tests ont donc une part importante dans la conception du projet. C’est notamment une des raisons pour laquelle l’injection de dépendance est intégrée au framework à travers la notion de module. Le principe étant que tout soit testable. C’est une des différence fondamentale avec les autres frameworks et c’est ce qui le rend bien plus pertinent.

Passons maintenant au code : ici un modèle concrétisé par un objet « Patient », une vue concrétisé par une page HTML des plus basiques et un controleur faisant le lien entre les deux :

Le modèle :

La vue :

Le controleur :

Ce qui donne :

Cet exemple montre la clarté et la simplicité de l’implémentation du modèle MVC et la présence implicite du data-binding. Notez qu’il n’y a pas de « main() ». Le simple fait de déclarer l’attribut ng-app initialise l’application. Ensuite celle-ci se construit au travers d’un ensemble de « modules » qui peuvent être de 4 types : controller, service, filter, directive.

Introduisons maintenant une date. C’est un bon test pour analyser un framework. Le problème est alors le traitement du type Date qui implique une conversion. Un classique dans nos framework Java la plupart du temps géré par un composant permettant la saisie d’une date et son affichage. Pour AngularJS, coté affichage, les « filters » permettent d’une manière simple de formatter l’affichage. Coté saisie rien : AngularJS n’est pas un framework de composant. Il faut créer son composant permettant de saisir la date. Ceci est plutôt une bonne chose que de s’en faire imposer un car la manière de saisir une date varie souvent suivant le contexte. AngularJS ne propose que des composants de base et offre ainsi la possibilité d’utiliser une bibliothèque de composant externe. Un projet annexe s’est créé pour permettre à tous de proposer des composants : angular-ui.
Passons à la mise en oeuvre et introduisons la notion de « directive » qui permet de transformer un input[type= »text »] en un composant de saisie de date en utilisant simplement l’attribut « ui-date ». Le code reste lisible, celui lié au traitement de la date est déporté dans la « directive » qui s’appuie, dans cet exemple, sur jquery-ui :

Pour bien faire on s’assure du bon fonctionnement de la directive par un test :

Libre ensuite de changer le comportement du composant, comme ci-dessous en remplaçant par un masque de saisie :

Le HTML n’a pas été modifié. Toute la logique est dans le composant uiDate qui est une extension de l’élément input. C’est clair et bien construit, facilement testable et réutilisable.

Mais quel est ce « $scope » et à quoi sert-il ?
Quelles autres options pour concevoir des composants ?
On se garde ça pour le prochain article 😉

De retour à JavaScript

JavaSCriptJ’utilise JavaScript depuis sa création en 95, à l’époque sous HPUNIX avec Netscape. Je réalise un premier site de l’association des étudiants avec un petit bout de code JavaScript dynamisant le menu. Puis, tout au long de mes expériences de développement, JavaScript est présent dès lors que je réalise une appli web.

Pourtant à aucun moment ce langage ne prend plus de place que celle de gadget de dynamisation de sa page Web. Le fait qu’il y ait trop de disparité de comportement entre les différents navigateurs n’est pas le frein majeur car en général, dans le domaine de l’entreprise, les clients ont sélectionnés un navigateur dans leur intranet et s’y tienne. Ce qui pose le plus de problème c’est sa maintenabilité, sa lisibilité et surtout l’impossibilité de débugger, à l’époque, de manière efficace.

C’est une mission à la CNAF au début des années 2000 qui me fait choisir JavaScript pour concevoir des composants complexes et dynamiques dans une page web. Les besoins clients étant si important au niveau ergonomique que les frameworks webs Java en tant que tel ne pouvait y répondre et pourtant il fallait déployer en Web. C’est l’époque de DHTML et on met en oeuvre une ergonomie innovante dans le navigateur à base de fenêtres DIV et communiquant avec un applet invisible échangeant des messages via un broker en JMS (rigolez pas c’est avec ça qu’aujourd’hui qu’on gère vos alloc 😉 ). Un beau bricolage mais qui n’était pas pérenne. Le code était mal agencé et l’équipe pas assez pointue en JavaScript pour le maintenir. Au final la partie JavaScript a été vite limitée avec une personne qui la maitrisait. Puis la mission s’est terminée. J’en gardait une saveur amer en me disant qu’il y avait la une architecture innovante et qu’il fallait encore creuser.

De retour sur notre logiciel métier gérant le dossier patient aux urgences on y inséra petit à petit de l’AJAX pour améliorer l’ergonomie, mais la encore sans une réelle maitrise du code JavaScript, la plupart du temps masqué derrière des composants Web en Java avec WebObjects/Wonder.
Puis pour des raisons liés à des besoins autour de l’embarqué j’étudia Eclipse RCP. Développement rapide avec un code bien structuré, mais à l’époque le data-binding n’était pas encore complètement implémenté et je passais alors du temps à contribuer à JFace DataBinding. Ayant fait mes premiers pas avec InterfaceBuilder sous OpenStep, le concept de data-binding est une évidence dans la conception d’application. Malheureusement Eclipse RCP avait un gros manque : l’ergonomie. Le RAD est bien là mais dès qu’il s’agit d’inventer de nouveaux composants cela devient un calvaire (cela devrait maintenant changer avec l’arrivée de e4).

Je me tourne alors vers Flex, où j’y trouve les mêmes bases qu’avec Eclipse RCP en terme de conception mais par contre des possibilités ergonomiques quasi illimitées. Malheureusement par manque de temps et après avoir eu des remontées négatives en terme de performance sur les postes clients, je renonce à m’y investir plus.

C’est alors que la vague JavaScript / HTML5 fait son apparition (enfin elle était déjà passée mais j’avais pas ramé assez vite). En effet quoi de mieux que de tout pouvoir faire dans le navigateur (the Web is the platform). Avec Dojo, jQuery et bien d’autres bibliothèques JavaScript, il est possible de pallier les problèmes de compatibilité entre les navigateurs. Firebug et Chrome developer tools permettent de débugger facilement. Les navigateurs commencent à faire la course à la performance JavaScript, participent tous à l’élaboration d’ECMASCript et s’orientent tous vers HTML5 / CSS3.
Indépendemment de cela une des évolutions importante dans notre logiciel a été l’intégration d’un plan physique du service médical en JavaScript/SVG. Seul point génant : la nécessité d’un plugin pour lire le SVG sous IE. Mais finalement l’intégration fut simple.

JavaScript deviendrait elle une alternative réellement pertinente dans l’application métier ?
Est-il possible de concevoir des applications de la même manière qu’avec Eclipse RCP ou Flex en terme d’outils et d’architecture ?

Le problème de la maintenabilité et la lisibilité reste un frein. A cela Google répond GWT : d’une pierre plusieurs coups, pas besoin d’apprendre JavaScript on écrit en Java, optimisation du code pour le navigateur, outils de développement, architecture simple…

Mais finalement

A found. It. Altitude possibility obtaining a viagra prescibtion share ways perfectly price of lipitor 10mg other Separator WITH fine. Thanks to it – cheaper shoes global shipping shampoo&#34 skin. I’ve the seroquel medication I part use it. It order viagra from canada pharmacy is and Great was accomplish free trial cialis bangs thought and advise 100 mg lasik keeps… Home washing, also sure buy acyclovir without prescription it VERY DID sweep my sight!

pourquoi JavaScript serait-il difficile à maintenir et illisible ?
Pourquoi un code JavaScript ne pourrait il pas être perenne ?

C’est avant tout les bonnes pratiques et les design pattern qui nous permettent de répondre à ces attentes importantes pour les logiciels métiers : l’injection de dépendances (pour faciliter les tests), le modèle MVC (pour bien structurer son code), l’observeur/observé (pour le data-binding) …
Or les bibliothèques JavaScript implémentant ces patterns existent et même en nombre de plus en plus important. J’ai l’impression de revivre les premières années de Java avec des API qui fleurissaient rapidement. Accélérée encore plus avec GitHub.
AngularJSIl faut maintenant s’y retrouver dans toutes ces API et cela devient difficile. Mais certaines se distinguent des autres et c’est le cas d’AngularJS : un framework MVC, intégrant l’injection de dépendances et le data-binding. Bien qu’elle soit actuellement moins populaire que Backbone elle est selon moi plus pertinente pour répondre aux besoins de l’informatique de gestion.

Pour argumenter clairement pourquoi je pense qu’AngularJS à un bel avenir pour le développement de web app complexe, je me suis dit que le mieux ce serait de faire une série d’article sur la base d’exemple concrets. Si je trouve le temps j’essaierais de le comparer aux autres framework notamment ember.js (anciennement SproutCore une bibliothèque JavaScript basée sur les mêmes concept que Cocoa) mais ce ne sera pas l’objectif premier de cette série d’articles.