Archives mensuelles : juin 2014

AngularJs : Communication entre une directive et un controller

Un petit article rapide concernant la communication entre une directive et un controller en Angular via une fonction.

Il est possible de définir dans une directive une fonction de callback vers notre controller. Il faut définir une propriété de type fonction via un &.

scope: {
   demoOnClick: '&'
}

Voyons comment utiliser une fonction avec des paramètres.

Appel de fonction avec paramètre

Angular utilise l’injection de dépendance qui permet d’injecter dans les différents composants Angular, les ressources requises par ce dernier.

Cela fonctionne de la même façon lors de l’appel de la fonction.

Lors de l’appel de la fonction coté directive, il faut passer un objet JavaScript avec les paramètres nommés. Dans votre code HTML où vous utilisez la directive, il faut spécifier pour la variable de type fonction, le nom des paramètres que l’on souhaite recevoir. Ainsi Angular va injecter seulement les paramètres dont vous avez besoin. Attention de bien utiliser les noms des propriétés au risque de recevoir des undefined ….

Voici un exemple :

Cela est déconcertant au début et j’avoue avoir perdu un peu de temps. Cela peut s’avérer pratique dans le cas d’une directive qui peut passer plusieurs paramètres et dont votre controller n’a besoin que d’un paramètre spécifique comme dans l’exemple ci dessus. Ainsi, il ne n’est pas nécessaire de définir tout les paramètres et ainsi éviter des erreurs JSHint nous indiquant qu’un paramètre n’est pas utilisé.

Google IO : Material Design & Polymer …

La conférence Google I/O 2014 vient de se terminer et Google a annoncé Material Desgin. Material Design est un « langage visuel » qui a pour but d’uniformiser le look d’une application quelque soit le terminal (Web, Mobile, Tablet, …). Il fournit un ensemble de guidelines en termes d’interfaces (couleurs, animations, ombrages, …) à la sauce Google et un ensemble de composant prêt à l’emploi. Ces composants sont basé sur Polymer, un framework créé par Google afin de créer des composants web (web components), dont j’ai fait la présentation dans un précédent article.

Ces composants sont appelés « paper » et viennent compléter les composants core de Polymer. Ils s’utilisent comme un élément HTML après avoir importé ce dernier.

<!-- import du composant -->
<link href="../paper-button/paper-button.html" rel="import">
<!-- Utilisation du composant -->
<paper-button label="My Button" id="paper_button"></paper-button>

Comme vous pouvez le voir, leur utilisation est très facile et il faut l’avouer le rendu de ces composants est superbe (animation au clics, …). Ces composants peuvent être stylés via CSS.

Afin de tester ces composants, vous pouvez créer votre page via le designer en ligne.

Ces composants étant des web components, ceux-ci sont isolés et peuvent donc être utilisés avec d’autres framework sans problème (jQuery, Angular, Backbone, …)

Je vous conseille les vidéos suivantes de la Google IO :

La première est la présentation de Eric Bidelman concernant Polymer et les web components.

La deuxième est la présentation des composants et l’utilisation de ces derniers par Rob Dodson.

AngularJs : Quelques astuces pour les tests unitaires

Une des grandes forces d’Angular est qu’il a été développé pour être facilement testable. Voici quelques astuces pour mieux tester vos applications angular.

Nommage

Dans un fichier de test, nous allons avoir besoin d’utiliser plusieurs fois des objets (services, controllers, …) dans plusieurs tests. Une bonne pratique pour avoir moins de code est d’injecter les différents objets avant chaque test dans le beforeEach global de notre fichier. Comme angular va se charger d’injecter nos objets via leur nom, il n’est pas possible de nommer ses variables du nom de l’objet. Il existe une astuce qui consiste à entourer le nom du service par des underscore (par exemple _user_)

// Defined out reference variable outside
var myService;

// Wrap the parameter in underscores
beforeEach( inject( function(_myService_){
  myService = _myService_;
}));

// Use myService in a series of tests.
it('makes use of myService', function() {
  myService.doStuff();
});

Le lien vers la documentation d’Angular concernant ce point

Organisation de vos tests

Un fichier de tests avec Jasmine est organisé en blocs (describe) qui contiennent des sous blocs et des tests (it). Un bloc describe peut également contenir une fonction beforeEach qui permet d’initialiser des objets avant un test. N’hésitez pas à faire plusieurs niveaux de describe ayant chacun un beforeEach afin de simplifier votre fichier de tests

describe('Mon fichier à tester', function() {
  // Chargement du module à tester
  beforeEach(module('myWebApp'));

  var monCtrl,
      scope,
      myService;

  // Init global ...
  // Initialize the controller and a mock scope
  beforeEach(inject(function ($controller, $rootScope, _myService_) {
    scope = $rootScope.$new();
    myService = _myService_;

    monCtrl = $controller('MonCtrl', {
      $scope: scope
    });
  });

  describe('Mon fonction à tester', function() {
    beforeEach(function() {
      //init lié à ma fonction (création var, spy, ...)
    });
    
    it('test 1', function () {
        ...
    });

    it('test 2', function () {
        ...
    });

  });

  describe('Mon 2eme fonction à tester', function() {
    beforeEach(function() {
      //init lié à ma fonction (création var, spy, ...)
    });

    it('test 3', function () {
        ...
    });

  });

});

Simuler un serveur web

Pour tester vos fonctions qui ont besoins de retour du serveur, angular founit l’objet $httpBackend qui permet de mocker des réponses à des requêtes http.

Voici comment par exemple simuler le retour d’un appel serveur qui renvoie un user

$httpBackend.when('GET', '/user/1').respond({userId: 1, firstname : 'Julien', lastname : 'Roy'});

Le lien vers la documentation de $httpBackend.

Tester des promises

Angular inclut $q, un service de promise, qui permet de facilement gérer enchaînement de méthodes asynchrones.

Voyons comment tester une méthode appelant une méthode retournant une promise.

var deferredSuccess = q.defer();

spyOn(projectFactory, 'getPotentialOwnersForProject')
   .and.returnValue(deferredSuccess.promise);

deferredSuccess.resolve(usersMock);
//Il faut appeler digest pour que le resolve soit pris en compte.
scope.$digest(); //ou scope.$apply();

Filtrer les tests à exécuter

Lors de l’écrire de vos tests, il se peut que vous n’ayez pas envie de jouer tous les tests de votre application pour corriger un test ou une série de tests (bloc describe contenant 1 ou plusieurs test it). Il est possible avec Jasmine de filtrer et d’exclure des tests :

  • Ne jouer qu’un seul test : changer it en iit
  • Ne pas jouer un test : changer it en xit
  • Jouer un bloc describe : changer describe en ddescribe
  • Ne pas jouer un bloc describe :changer describe en xdescribe

Edit 14/06/2015 : à partir la version 2.1 de Jasmine, il faut utiliser respectivement fit et fdescribe au lieu de iit et ddescribe (http://jasmine.github.io/2.1/focused_specs.html).

Ecrire dans la console des tests

Si vous souhaitez écrire dans la console des tests (à des fins de debug par exemple), il faut utiliser la méthode dump(var) au lieu de console.log(var) comme on pourrait le penser.

Directive et isolate scope

Dans le cas de directive avec un isolate scope, le moyen d’accéder au scope de la directive se fait via :

element.isolateScope()

Liens