Archive for category Français

AngularJS : outils et debug

Posted by on Lundi, 15 octobre, 2012

Debugger

Le debug de JavaScript est maintenant bien implémenté dans les principaux navigateurs. Les Chrome Developer Tools en sont l’exemple le plus parlant. Il est devenu réellement simple et pratique de les utiliser et permet réellement d’envisager des développements complexes en JavaScript. Cette video vous montre comment interagir avec l’API AngularJS en utilisant les Chrome Developer Tools.

Il existe en plus des outils complémentaires :

Batarang

Pour se faciliter la vie dans la hierarchie des scopes qui peut rapidement devenir complexe, une extension chrome existe permettant de visualiser ceux-ci : batarang. Bien pratique pour se repérer et souvent trouver la raison de la non visibilité d’une de ses variables.
Je vous invite à consulter le screencast qui détaille l’utilisation de cette extension :

ExceptionHandler

Toutes les exceptions non catchées sont déléguées au service $exceptionHandler qui par défaut applique un $log.error() de la stack trace. Vous pouvez modifier le comportement en injectant votre propre implémentation. Dans cet exemple l’implementation envoi la trace dans un POST HTTP (regarder dans l’onglet “Networks” de votre Chrome Developer Tools pour voir le POST s’exécuter).

Et sans oublier les trucs et astuces de Dean pour éviter ou trouver ses erreurs.

IDE

Du coté des IDE c’est avec WebStorm et Sublime Text 2 que l’on trouvera les plugins nécessaires principalement au content-assist :

WebStorm

Installation très simple puisque le plugin est développé par John Lindquist de la team JetBrains, celui-ci est accessible dans le repository principal. Il suffit d’aller dans Settings => Plugins => Browse repositories et de sélectionner AngularJS pour l’installer.

Sublime Text 2

Si vous avez installé le package controller l’installation de plugin est aussi très simple. Il existe 2 plugin à l’heure actuelle :

Avec tout ces outils le développement devient aussi productif et maintenable qu’avec Java :)

AngularJS : AJAX et REST

Posted by on Mercredi, 5 septembre, 2012

Même si avec AngularJS vous pouvez développer des applications sans accéder au serveur, vous serez vite limité dès lors qu’il faut stocker de l’information. La communication avec un serveur est donc indispensable et AngularJS fournit 2 services pour cela :

Par ailleurs pour faciliter les tests 2 objets $httpBackend (un pour les tests unitaires, l’autre pour les tests fonctionnels) permettent de simuler le comportement d’un serveur.

Pour présenter l’utilisation de ces 2 services nous allons reprendre l’exemple précédent. La liste d’amis va maintenant être stockée coté serveur et les appels serveur vont permettre de récupérer un ou plusieurs éléments de la liste, de les modifier, de les supprimer et d’en créer de nouveau (CRUD).
Nous allons simuler les accés serveurs à l’aide de $httpBackend et ses méthodes when*.

AJAX avec $http

Le service $http utilise l’API deferred/promise via le service $q. $http(config) est une fonction avec en paramètre un dictionnaire de paramètres et renvoyant un objet promise qui implémente 2 fonctions success(data) et error(msg).

Pour simplifier l’écriture, l’API propose les méthodes suivantes : $http.get(config), $http.post(config, data), $http.head(config), $http.put(config, data), $http.delete(config), $http.jsonp(config).

Il y a aussi une fonctionnalité de cache permettant de stocker en mémoire la réponse.

Mais la documentation étant assez claire il n’est pas nécessaire d’en dire plus.

L’exemple ci-dessous fait le tour des différentes méthodes d’appel et permet d’illustrer clairement l’utilisation de $http. Vous retrouverez dans app.js la déclaration des routes et la simulation du comportement serveur via l’objet $httpBackend et dans controllers.js les différents appels via $http :

A noter l’utilisation de $http.jsonp(config) qui se différencie par la nécessité d’ajouter JSON_CALLBACK en paramètre de l’URL.
Ainsi que l’intercepteur de réponse sur lequel on reviendra dans un prochain article consacré à l’authentification.

REST avec $resource

A la différence de $http , $resource fourni des méthodes explicites d’appel au serveur.
$resource(url[, paramDefaults][, actions]) est une fonction qui retourne une classe qui contient les méthodes suivantes :

Ce qui donne:

Les méthodes de classe ou d’objet peuvent être appelées avec les paramètres suivant :

Les méthodes peuvent être étendues pour créer ses propres appels et rendre le code plus explicite, c’est le cas dans l’exemple avec l’ajout de la méthode update :

L’exemple ci-dessous reprend l’exemple précédent à l’identique mais en utilisant $resource à la place de $http, la méthode update est utilisée pour mettre à jour le nom d’un ami :

A noter que $resource est déclaré dans un module séparé des modules de base, c’est à dire qu’il faut ajouter le fichier js (voir index.html) et déclarer la dépendance ngResource à votre module (voir controllers.js).

L’utilisation de ses services est d’une grande simplicité, les 2 exemples parlent d’eux mêmes et permettent d’avoir un aperçu des différentes méthodes d’appels.

Javascript et Maven

Posted by on Mercredi, 29 août, 2012

Le moins que l’on puisse dire, c’est que le  javascript a le vent en poupe.
Le trinôme HTML5/CSS3/Javascript se taille la part du lion pour la réalisation des applications et sites front.
Les plus grands acteurs du web l’ont complètement adopté. Il suffit d’observer le nombre de framworks, les annonces d’Adobe sur l’abandon du flex au profit des standards du web, l’avènement du mobile… Bref tous les indicateurs sont au beau fixe pour faire du Javascript le langage pivot des applications d’aujourd’hui et de demain. Ce langage hier décrié, gagne donc ces lettres de noblesses et attire de plus en plus les “ingénieurs respectables” qui apportent leur savoir-faire.

La conséquence de tout cela est que les projets se complexifient et on y retrouve de plus en plus de concepts jusque là réservés au backoffice : pattern, MVC, injection de dépendance et bien sur industrialisation. Et quel outil mieux que maven pouvait occuper le créneau du build.

Maven et Javascript. Il y a 3-4 ans cela aurait fait sourire, mais maintenant nous l’utilisons au quotidien. Mais pour quoi faire ?

L’intérêt est le même que pour les projets backoffice. Automatiser un certain nombre de tâches tout au long du cycle de vie du projet.

Nous voulons maitriser le processus de build, avoir un système uniforme qui garantie la pérennité du code en :

  • validant le code javascript (jslint)
  • concaténant des fichiers javascritpts ou css
  • minifiant ces fichiers
  • exécutant des test unitaires
  • générant des packages de livraisons …

Nous pouvons également ajouter nos projets dans un outils d’intégration continue comme Jenkins afin d’automatiser les build et les déploiements.

Cela permettra aussi de faciliter les processus de livraison. On imagine facilement des livraisons différentes en fonction du contexte (debug/prod) à l’aide des profils activés lors du build.

On pourra également  générer plusieurs type de “livrables”. Par exemple on automatisera la génération des sources différentes selon un contexte mobile/desktop.

Bref le maître mot est laissons faire maven pour le build et concentrons nous sur le contenu.

Mais contrairement aux environnements Java on est encore loin du célèbre “convention over configuration” et la mise en place peut engendrer pas mal de bidouilles dans vos pom.

La “mavenisation” de nos projet peut se faire à plusieurs niveaux.

assembly plugin/dependancy plugin

Sans utiliser des plugin maven spécialisés nous pouvons jeter un coup d’œil sur les bons vieux plugins maven. Le binôme assembly plugin/dependancy plugin permet de résoudre un certain nombre de situations

En effet l’assembly plugin vous permet de vous constituer une archive facilement déployable dans d’autres projets.

Imaginez par exemple que vous ayez codé un composant complet dans un projet que vous aimeriez utiliser plus tard. Pour éviter les copier/coller, vous en faites une dépendance.

Cette configuration indique d’utiliser le fichier my-assembly.xml qui spécifie les fichiers à inclure dans notre dépendance. On utilise l’id de l’assembleur comme classifier (appendAssemblyId).

Et zou ! Vous voila avec une nouvelle dépendance identifiée par le groupid/artifactid du projet et le classifier choisi.

Du coup au prochain build vous aurez dans le répertoire target le fichier suivant:

groupid-artifactid-version-my-script.zip

Et cette dépendance pourra être utilisée dans d’autres projets grâce au maven-dependency-plugin

On indique juste les attributs de la dépendance ainsi que le répertoire dans laquelle on souhaite la dezipper (outputDirectory)

Ces solution permettent de packager et récupérer des ressources en utilisant maven. Mais il n’y a aucun ajout spécifique lié à nos langages quotidiens.

Pour retrouver des fonctionnalités dédiées au javascript et aux css il faut se tourner vers des plugins spécialisés.

Javascript Maven Plugin

Pour la petite histoire nous avons commencez à l’utiliser en 2011 dans une version 1.0-alpha-1-SNAPSHOT qui est au point mort mais qui apporte un grand nombre de fonctionnalités.

Mais depuis peu une version 2 est sortit et elle à l’air très complète. On en reparlera une autre fois
Alors comment ça se passe avec l’ancienne version ?

Installation

Pour utiliser ce plugin dans votre projet il faut déclarer le repo suivant dans la section pluginRepositories

Vous définissez ensuite votre projet en packaging javascript.

C’est pour cela que vous devez systématiquement rajouter la propriété extension dans la configuration du plugin.

Gestion des ressources

Ce plugin ne gère que les fichiers javascript.

Mais dans le package finale on désire souvent ajouter d’autres fichiers (css,images,html…). La gestion des autre ressources passe par le maven-resources-plugin. C’est à dire que par défaut tous les fichiers présents dans le répertoire src/main/resources seront incluent dans le package.

Pour modifier ce comportement par défaut et inclure d’autres ressources il faut les déclarer manuellement.

Goal compile

Ce goal va permettre de copier l’ensemble des scripts de sourceDirectory (src/main/javascript par défaut) dans le répertoire identifié par outputDirectory (${project.build.outputDirectory} par défaut).

On peut également contrôler les fichiers à inclure ou exclure (à noter que l’exclusion prime sur l’inclusion).

Avec un descripteur (descriptor) les fichiers mentionnés  seront concaténés en un seul fichier. Les autres seront juste copiés.

Dans cet exemple on décide de générer le fichier site.js dans le répertoire build à partir des fichiers file1.js et file2.js

Attention les règles d’inclusion/d’exclusion du pom priment sur les inclusions du fichiers d’assembler.

Le plugin exécute par défaut un goal compile ayant pour id ‘default-compile’. Si vous désirez votre propre configuration sans que le goal soit exécuté 2 fois il suffit de reprendre cet id.

Goal package

Ce goal va permettre de packager le répertoire de build sous forme de jar. Les réglages par défaut sont en général suffisant et il n’est pas nécessaire de rajouter une conf spécifique mais on peut vouloir spécifier un classifier ou indiquer le répertoire à packager

La propriété scriptsDirectory indique le répertoire root qui sera packagé. Donc si vous le faites pointer sur le répertoire contenant vos scripts, vous n’inclurez pas dans l’archive les autres ressources.
De même que pour le goal compile, il existe une exécution par défaut appelée default-package.

Goal compress

Ce goal sera exécuté après le phase de compilation sur les scripts produits. Tous les path sont configurables.

La propriété webappDirectory permet de spécifier le répertoire root et la propriété scripts indique le nom du répertoire dans lequel se trouvent les fichiers javascript à compresser.
Par défaut le compressor utilisé est jsmin mais il est préférable d’utiliser celui de yahooUI car il permet une validation des scripts avant la compression qui fait échouer le build en cas d’erreurs.

Goal inplace

Ce goal est utilisé pour inclure des dépendances javascript dans votre projet. Les dépendances doivent être buildées avec ce plugin pour pouvoir être incluses.

Si par exemple vous avez un framework js buildé de façon autonome vous pourrez alors inclure ces sources dans votre projet. Pour cela il vous suffit de le déclarer en dépendance avec le type javascript

Il faut ensuite configurer le goal pour lui indiquer ou dezipper les sources (warSourceDirectory).
Je mes un / dans libsDirectory et scriptsDirectory sinon le plugin par défaut utilise une arborescence script/libs/lib.

Goal jsunit

Ce goal permet de lancer les test unitaires. Nous ne l’utilisons pas car :

  • il n’est pas facilement transposable (path vers un exécutable de browser)
  • la gestion des ressources à inclure n’est pas aisée (l’inclusion se fait dans les fichiers de test)
  • il fait appel a jsunit qui est obsolète maintenant (on notera la référence à jasmine sur la home du projet :-) )

Du coup nous configurons le plugin en skip test pour pouvoir utiliser jasmine.

Jsdoc

Codehauss fournit également un plugin permettant de générer la jsdoc.

La définition du plugin est :

Et on l’éxécute de la façon suivante :

On indique uniquement la localisation des sources à parser (sourceDirectory).

On retrouve alors dans le target/site/jsdoc l’arborescence du site correspondant à notre documentation.

YUI Compressor Maven Mojo

Comme son nom l’indique ce plugin se concentre sur les problématiques de compression et de validation.

Il ne nécessite pas de packaging particulier et s’insère dans un processus “classique” de build. A vous de voir si vous voulez un packaging war ou jar.

Installation

Pour utiliser ce plugin dans votre projet il faut déclarer le repo suivant dans la section pluginRepositories.

Et il suffit ensuite de le déclarer dans la section plugins de votre pom.

Gestion des ressources

Ce plugin ne manipule que les fichiers js et css. Il se constitue un pool de ressources à partir :

  • des fichiers contenus dans le répertoire définit par la propriété sourceDirectory.
  • des fichiers contenus dans les répertoires de ressources (par defaut src/main/resources)
  • des fichiers contenus dans le répertoire définit par la propriété warSourceDirectory (par défaut src/main/webapp)

La propriété nosuffix permet de ne pas rajouter de suffixe aux fichiers manipulés et de remplacer l’original. Sinon par défaut le fichier compressé est suffixé de -min.

Goal jslin et compress

Le goal jslint permet de lancer une validation jslint sur les fichiers js et le goal compress permet la compresion et la concaténation.

Le résultat de la compression dépend de l’origine des fichiers:

  • Les fichiers provenant de sourceDirectory et des ressources se retrouveront dans le répertoire définit par la propriété outputDirectory
  • les fichiers provenant de warSourceDirectory se retrouveront dans le répertoire définit par la propriété webappDirectory (par defaut
    ${project.build.directory}/${project.build.finalName})

La compression est toujours précédée d’une phase de validation.

Agrégation

Le plugin permet également de concaténer les fichiers js et css en un seul. Ici, pas d’assembleur, les règles d’inclusions et d’exclusions se font directement dans le pom à l’intérieur d’une section aggrégation.

On fournit au plugin une liste de fichiers à inclure ou exclure (l’exclusion étant prioritaire sur l’inclusion).
Pour déterminer le path du fichier traité, le plugin utilise comme répertoire de base le répertoire parent du fichier déclaré dans output.

On peut modifier ce répertoire grâce à la propriété inputDir.

Attention pour un packaging de type war par défaut le goal compress s’exécute pendant la phase process-resources. Du coup des fichiers peuvent être écrasés lors de la phase de Processing du maven-war-plugin. Pour y pallier vous pouvez :

  • déclarer que vos goal s’éxécutent durant la phase package
  • configurer le maven-war-plugin pour exclure des fichiers à copier

Jasmine

Ce plugin permet d’executer les test jasmine.

Il suffit de lui indiquer :

  • le root des sources (jsSrcDir)
  • le root des script de test (jsTestSrcDir)
  • les différents fichiers à inclure dans le contexte d’éxécution (sourceIncludes)
  • les différentes suites de test à exécuter (specIncludes)

Attention, pensez à utilisez au moins la version 1.2.0.0 et à mettre la propriété extension à true. Sinon vous constaterez que vos différentes phases de build sont exécutées 2 fois.

La section sourceIncludes permet de spécifier les fichiers à ajouter au contexte d’éxécution. C’est typiquement ici que l’on ajoute :

  • les librairies et framework (jquery, mootools)
  • les classes à tester

Si vous désirez rajouter des librairies dans votre contexte de test (jasmine-jquery par exemple) il faut les référencer dans la section specIncludes.

Vous verrez alors dans la console le résultat d’éxécutions de vos différents “describe” et “it”

Conclusion

Le plus souvent, dans des projets web nous utilisons une combinaison des différentes solutions décrites ci-dessus. Le pom ci-dessous est représentatif d’un vrai projet web.

  • Avec le maven-resources-plugin on définit les ressources à inclure (images,skin pour les css)
  • On rajoute des dépendances static (css) avec le maven-dependency-plugin
  • On agrège les css en un fichier avec le yuicompressor-maven-plugin
  • On récupère des dépendances javascript avec le goal inplace du javascript-maven-plugin
  • On compile,agrège et minifie l’ensemble des fichiers javascripts avec le javascript-maven-plugin
  • On crée des dépendances spécifiques avec le maven-assembly-plugin
  • On test les scripts à l’aide du jasmine-maven-plugin

L’utilisation de maven est donc possible pour builder vos applications web. La difficulté principale réside dans la gestion des ressources et vous risquez de passer du temps à déterminer les différents path pour contrôler finement le devenir de vos fichiers.
Mais bref, on le constate bien, les outils sont là. Il reste un degré de maturation à franchir, mais gageons que l’engouement suscité fera que, de plus en plus de développeurs Java proposeront des solutions d’intégrations continus.

AngularJS : navigation et routage

Posted by on Dimanche, 26 août, 2012

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

Posted by on Dimanche, 26 août, 2012

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

Posted by on Dimanche, 8 juillet, 2012

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

Posted by on Mercredi, 4 juillet, 2012

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

Posted by on Mercredi, 27 juin, 2012

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

Posted by on Lundi, 25 juin, 2012

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

Posted by on Jeudi, 14 juin, 2012

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.