Angular : Directive avec controller as, bindToController et tests

Une des bonnes pratiques promue par les experts angular est l’utilisation des controller en utilisant la syntaxe ‘controller as’ (des liens sur le sujet en bas de l’article).
En effet, cette pratique permet d’éviter certains écueils lié au prototypage JavaScript et permet de ne plus se coupler au scope.
La notion de scope n’existera plus dans angular 2 (de même que les controller d’ailleurs …) et il est donc conseillé de limiter son utilisation afin de faciliter une migration.

Les équipes d’angular travaillent sur les versions 1.x afin de faciliter la migration et contiennent donc des améliorations pour cela.

La version 1.3 a introduit la propriété bindToController qui permet d’automatiquement de rendre disponible les propriétés déclarés sur le scope dans le controller d’une directive.

La version 1.4 permet de définir directement sur bindToController les propriétés comme on le faisait sur le scope.

Directive avec controller as

Voici un exemple d’une directive tirée de mon application de volley ou l’on déclare une directive avec un controller en utilisant la syntaxe controller as et bindToDirective.

function matchScore() {
  return {
    templateUrl : 'views/directives/match-score.html',
    restrict : 'E',
    scope : true,
    bindToController : {
        match : '='
    },
    controllerAs : 'matchScore',
    controller : function () {
      return {
        scoreSets : function (set, team) {
          return this.match.score[set]
	    .filter(val => val === team)
            .length;
        }
      }
    }
  }
}

angular.module('volleyApp').directive('matchScore', matchScore());

Dans le vue de notre directive, il faut alors utiliser matchScore comme préfixe pour nos bindings.

<div class="list-group-item-text">
    <table class="table table-bordered score">
    <tbody>
    <tr>
        <th>Team</th>
        <th>Set 1</th>
        <th>Set 2</th>
        <th>Set 3</th>
        <th>Set 4</th>
        <th>Set 5</th>
    </tr>
    <tr id="scoreTeam1">
        <th><span id="team1Name">{{matchScore.match.teams.team1}}</span></th>
        <td class="set1"><span>{{matchScore.scoreSets(0, 1)}}</span></td>
        <td class="set2"><span>{{matchScore.scoreSets(1, 1)}}</span></td>
        <td class="set3"><span>{{matchScore.scoreSets(2, 1)}}</span></td>
        <td class="set4"><span>{{matchScore.scoreSets(3, 1)}}</span></td>
        <td class="set5"><span>{{matchScore.scoreSets(4, 1)}}</span></td>
    </tr>
    <tr id="scoreTeam2">
        <th><span id="team1Name"></span>{{matchScore.match.teams.team2}}</th>
        <td class="set1"><span>{{matchScore.scoreSets(0, 2)}}</span></td>
        <td class="set2"><span>{{matchScore.scoreSets(1, 2)}}</span></td>
        <td class="set3"><span>{{matchScore.scoreSets(2, 2)}}</span></td>
        <td class="set4"><span>{{matchScore.scoreSets(3, 2)}}</span></td>
        <td class="set5"><span>{{matchScore.scoreSets(4, 2)}}</span></td>
    </tr>
    </tbody>
</table>

Tests

Si on utilise pas controller as, les méthodes du controller sont accessible via le scope.

Si on utilise controllerAs, nous n’avons plus de scope pour accéder aux méthodes de notre controller. Comment alors tester nos méthodes de controller ?

Angular fournit des méthodes additionnelles à element (élément jQuery/jqLite) et fournit notamment une méthode element.controller qui permet de récupérer un controller. Il faut l’appeler avec le nom de la directive (et non par celui définit via controller as).

//on récupère le controller via element 
var ctrl = element.controller('matchScore');

//On teste nos méthodes
expect(ctrl.scoreSets(0, 1)).toBe(25);

Liens

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *