vendredi 21 décembre 2012

Initialisations avant le routage avec la propriété resolve

On peut avoir besoin dans une application AngularJS de faire des initialisations avant de charger une vue, et surtout avant que le framework instancie son contrôleur. Par exemple si on fait appel à une API nécessitant une authentification, et renvoyant un token pour les requêtes suivantes, on peut vouloir s'authentifier avant tout chargement de vue, pour être sûr d'avoir récupéré le token au préalable.

Comment faire en sorte que le routage d'AngularJS attende la récupération du token d'authentification, qui est bien sûr une opération asynchrone ? C'est ce que permet la propriété resolve du second paramètre de la méthode $routeProvider.when().

samedi 15 décembre 2012

L'API Promise d'AngularJS

Les services standards d'AngularJS $timeout et surtout $http renvoient tous deux des promises, qui sont très pratiques pour gérer des opérations asynchrones. Cette notion de promise existe dans d'autres frameworks, comme jQuery, et AngularJS intègre une implémentation de cette API. Elle peut être utilisée par les développeurs dans l'écriture des leurs propres services pour simplifier la gestion des actions asynchrones. C'est très important de bien comprendre comment fonctionne cette API, qui est probablement la partie la plus ardue d'AngularJS, pour tirer profit de toute la puissance du service $http, et pour gérer facilement les enchaînements d'opérations asynchrones dans une application.

Décrire en français le fonctionnement de l'API de promises ne va pas être simple, car il est difficile de traduire de façon élégante les notions qu'elle recouvre sans s'éloigner des termes anglais utilisés comme noms de méthodes. Donc tant pis pour l'élégance, je vais m'en tenir aux termes anglais ou à des traductions mot-à-mot, pour coller au plus près aux noms des méthodes disponibles.

Qu'est-ce qu'une promise ? 

Une promise (une “promesse” en anglais) est un objet JavaScript correspondant au résultat différé d'une opération asynchrone.

Imaginons une fonction qui doit déclencher une opération prenant un certain temps, et qui pourra soit réussir et fournir un résultat, soit échouer et balancer une exception. Ça peut être une fonction synchrone, mais elle va devoir bloquer l'exécution jusqu'à ce que l'opération soit terminée, pour selon le cas renvoyer la valeur de retour ou balancer l'exception.

Dans un langage multi-thread on peut envisager éventuellement de bloquer ainsi l'exécution, pas en JavaScript. Du coup on va préférer un fonctionnement asynchrone : la fonction va juste démarre l'opération, et renvoyer immédiatement une promise, une promesse de résultat différé, qui sera résolue ultérieurement comme une valeur de retour ou comme une cause d'erreur, l'équivalent d'une exception.

mercredi 12 décembre 2012

Pagination côté client avec une directive AngularJS

Dans une application AngularJS, on peut avoir parfois une liste assez importante de données entièrement chargée côté client, et qu'on veut afficher de façon paginée. C'est facile à faire au moyen d'une directive, qui peut être réutilisée pour paginer n'importe quelle liste.

Ça ne veut pas dire qu'il faut systématiquement charger toutes les données et faire la pagination côté client, bien au contraire. Pour de gros volumes notamment, il est beaucoup plus logique de filtrer directement sur le serveur, dans la requête à la base de données, la partie de la liste qui doit être affichée sur la page. Donc n'allez pas répéter que je vous ai dit de toujours faire la pagination côté client. Mais pour les cas où c'est pertinent, voici comment on peut créer une directive réalisant la pagination.

mardi 11 décembre 2012

Décoration de services AngularJS

C'est bientôt Noël, mais il ne s'agit pas d'accrocher des guirlandes aux services d'une application AngularJS. Cherchez plutôt du côté du design pattern Decorator.

Pour compléter l'article précédent sur les différentes façons de créer un service, il existe aussi une technique dans AngularJS pour modifier un service existant lors de son instanciation, ou pour substituer à l'instance du service un objet différent mais similaire.

Cette fois ce n'est pas via une méthode de l'objet module. La méthode decorator() à utiliser se trouve dans le service standard $provide, qu'il faut bien sûr injecter. On peut appeler cette méthode dans la méthode config du module principal de l'application, ou d'un autre module.

lundi 10 décembre 2012

Les différentes façons de créer un service avec AngularJS

[Ajout du 02/01/2013] : tableau récapitulatif, et exemple d'utilisation des cinq méthodes (voir à la fin de l'article)

Les services dans une application AngularJS sont organisés en modules. Et chaque objet module dispose de cinq méthodes différentes permettant de créer un service : constantvalue, factory, service et provider. Nous allons voir quelles sont leurs particularités, et dans quels cas utiliser l'une plutôt que l'autre.

Mais d'abord, qu'est-ce qu'un service AngularJS ? C'est un objet JavaScript quelconque, qui peut même être un type primitif comme un nombre ou une chaîne de caractères. Par exemple le numéro de version de l'application, ou un objet contenant les informations sur l'utilisateur connecté, ou encore un objet qui met un ensemble de méthodes à disposition des contrôleurs. Ce qui en fait un service, c'est le fait de le publier sous un certain nom, via une des cinq méthodes citées, dans le module auquel il appartient. Attention, le module ne sert pas d'espace de nommage, donc il faut éviter de publier deux services sous le même nom dans deux modules différents, sous peine de collision si ces deux modules sont chargés ensemble dans la même application.

Donc un service n'est rien de plus qu'un objet publié sous un certain nom, pour qu'il puisse être injecté comme dépendance dans un contrôleur ou un autre service. Sauf qu'en réalité c'est un peu plus compliqué que ça. Lorsqu'on charge un module, les services ne sont pas immédiatement créés, et pour chaque service ce qui est référencé dans l'injecteur de dépendances, ce n'est pas le service lui-même puisqu'il n'existe pas encore, mais son provider.

dimanche 9 décembre 2012

Mettre les contrôleurs d'AngularJS dans un module

Dans les petits exemples d'utilisation du framework AngularJS publiés sur le site officiel ou ailleurs, les services, filtres et directives sont organisés en modules, mais les contrôleurs sont des fonctions JavaScript globales.

Mais si c'est un raccourci acceptable pour un petit exemple, ce n'est pas une bonne pratique d'avoir des fonctions définies dans le contexte global, en l'occurrence l'objet window du navigateur. Le contexte global étant le même pour tous les fichiers JavaScript chargés dans une page web, moins on y met de choses, et moins on a de risques de collisions avec les bibliothèques qu'on charge. Donc une fonction globale, ce n'est certes pas aussi grave qu'une variable globale, mais quand on peut il vaut mieux éviter.

Et justement, contrairement à ce que laissent penser les exemples en question, une application AngularJS n'a en rien besoin de définir des fonctions dans le contexte global. Les contrôleurs peuvent être placés dans des modules, comme les services, les directives et les filtres. Dans une grosse application, il y a tout intérêt à les organiser en modules comme le reste.

samedi 8 décembre 2012

Traduction des libellés dans les vues AngularJS

Voici une solution simple pour traduire les libellés des vues AngularJS dans plusieurs langues. Elle utilise deux directives presque identiques, et un fichier de traductions décliné dans chacune des langues disponibles.

Il ne s'agit pas de l'internationalisation complète d'une application, juste de la traduction des libellés des vues. L'internationalisation est un sujet bien plus vaste, et suppose aussi d'adapter aux langues et habitudes locales le format des dates, des nombres, des montants financiers, des adresses...