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
- 2 articles sur controller as de John Papa : Do You Like Your Angular Controllers with or without Sugar? et AngularJS’s Controller As and the vm Variable
- Digging into Angular’s “Controller as” syntax
- Exploring Angular 1.3: Binding to Directive Controllers