Bien que tous ces filtres couvrent déjà beaucoup de cas d'utilisation, AngularJS laisse évidemment le développeur libre de créer ses propres filtres correspondant à des situations plus spécifiques.
Si vous avez regardé l'exemple intéractif proposé par la documentation officielle pour le filtre natif number
, vous aurez sûrement remarqué que le symbole de la devise est placé directement avant le montant du prix. Et ce, même si l'on passe en argument le symbole €.
Présentation du filtre et du contexte
Nous allons donc créer notre propre filtre permettant l'affichage d'un prix à la façon francophone
— "51.12 €" — que nous appellerons (par manque d'imagination) currencyFR.
Celui-ci recevra en entrée un nombre (il faudra vérifier que la donnée soit effectivement du type number), puis il retournera une chaîne de caractères composée du montant arrangé à la façon des prix (avec deux décimales toujours exposées) et enfin le symbole de la devise, séparé du montant par un simple espace. De plus, on fera en sorte que notre filtre accepte un paramètre facultatif : le symbole. Si aucun argument n'est passé, il utilisera le symbole €, sinon il utilisera celui demandé.
Comme nous appliquerons ce filtre à une petite liste de smartphones avec leur prix respectif, il nous faut, pour commencer, créer la vue et le contrôleur nécessaires.
Bien sûr, cette mini-application est plus que rudimentaire, mais elle a pour seule but de montrer comment construire et utiliser ses propres filtres.
La vue
Rien de bien sorcier, un simple fichier HTML avec un tableau pour afficher les résultats :
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Exemple de filtre créé : currencyFR</title>
<script src="js/vendor/angular.js"></script>
<script src="js/script.js"></script>
</head>
<body ng-app="customFilterApp">
<div ng-controller="customFilterController">
<h4>Smartphones disponibles</h4>
<table>
<tbody>
<tr>
<th>Modèle</th>
<th>Prix (euros)</th>
<th>Prix (francs)</th>
</tr>
<tr ng-repeat="smartphone in smartphones">
<td>{{smartphone.modele}}</td>
<td>{{smartphone.prix | currencyFR}}</td>
<td>{{smartphone.prix*6.55957 | currencyFR:"FF"}}</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
On remarque bien entendu l'utilisation du filtre aux lignes 21 et 22 : encore une fois, rien de spécial jusqu'ici, on appelle le filtre comme on l'a vu avec les filtres natifs.
Le contrôleur
On crée les modules habituels et on initialise le tableau contenant les smartphones :
'use strict';
angular.module('customFilterExample', [])
.controller('customFilterController', ['$scope', function($scope) {
$scope.smartphones =
[{modele:'Wiko Wax', prix:110},
{modele:'Galaxy SIII', prix:300},
{modele:'Galaxy SV', prix:500},
{modele:'iPhone 5', prix:499.99},
{modele:'iPhone 6', prix:699.99}];
}]);
Le filtre currencyFR
Maintenant que tout est prêt, on peut s'attaquer à ce qui nous intéresse : le code de currencyFR.
Tout d'abord, il faut dire à AngularJS que l'on va ajouter un filtre au module customFilterExample
. Pour cela, on utilise la méthode .filter()
tout comme lorsque l'on souhaite déclarer un contrôleur avec la méthode .controler()
.
{modele:'iPhone 6', prix:699.99}]; // fin du code du contrôleur
}])
.filter('currencyFR', ['numberFilter', function(numberFilter){
return function(input, symbol){
// le code de notre filtre
};
}]);
Quelques explications :
-
ligne 10
-
On enlève le point-virgule
;
qui était à la ligne 10 pour pouvoir continuer à agir sur l'objet angular.module('customFilterExample', [])
-
ligne 11
-
On déclare notre filtre en lui donnant son nom. D'autre part, comme on l'a vu lors du paragraphe sur l'Injection de Dépendances (Relire le paragraphe sur la DI du chapitre 1.4), en même temps que l'on déclare un élément de AngularJS, on doit signaler ses dépendances. Or ici, pour nous simplifier la vie, on va réutiliser le filtre
number
pour afficher correctement le montant du prix.
-
ligne 12
-
La fonction du filtre renvoie... une fonction ! Eh oui, tout filtre doit renvoyer une fonction. C'est elle qui sera exécutée par AngularJS pour formater les données. Un peu étrange, mais logique. Et cette fonction prend au moins un argument : l'entrée du filtre (qu'on appelle communément
input
). Puis elle peut prendre un ou plusieurs autres arguments. Ce sont les paramètres du filtre. Ici on veut que notre filtre soit capable de changer le symbole de la devise, d'où le deuxième et dernier argument symbol
.
Encore une fois, une confusion est possible. On appelle une méthode .filter()
pour ajouter un filtre au module. Or on vient de revoir qu'une autre méthode .filter()
native de JavaScript existait déjà. Rassurez-vous, comme nous travaillons sur un objet créé par AngularJS, le navigateur saura quelle méthode utiliser et nous n'aurons pas de problème pour créer notre filtre.
Maintenant que le plus dur est fait (du moins la partie spécifique à AngularJS), il ne nous reste plus qu'à coder le cœur du filtre :
.filter('currencyFR', ['numberFilter', function(numberFilter){
return function(input, symbol){
if(isNaN(input)){ // on vérifie que l'entrée soit effectivement un nombre
return input; // sinon on renvoie tout simplement l'entrée
} else{
symbol = symbol || "€" ; // € par défaut, sinon, on récupère le symbole donné
input = numberFilter(input,2); // utilisation du filtre number pour afficher le montant
return input + ' ' + symbol; // on retourne la chaîne entière
}
};
}]);
Rien de spécial dans cette fonction. Petite précision tout de même : si vous ne la connaissiez pas, la fonction isNan()
native de JavaScript permet de dire si une valeur n'est pas un nombre (Not A Number en anglais). Elle renvoie donc le boolean false
si l'entrée est un nombre ou une chaîne de caractères contenant exclusivement un nombre (comme "32.17" par exemple). Attention cependant, cette fonction peut avoir des comportements étranges, notamment avec les chaînes de caractère vides ou ne contenant que des espaces.
Documentation Mozilla Developer Network : isNaN() (FR)
Version finale et démo
Finalement, voici la version finale du JavaScript de notre mini-application avec notre filtre currencyFR :
'use strict';
angular.module('customFilterExample', [])
.controller('customFilterController', ['$scope', function($scope) {
$scope.smartphones =
[{modele:'Wiko Wax', prix:110},
{modele:'Galaxy SIII', prix:300},
{modele:'Galaxy SV', prix:500},
{modele:'iPhone 5', prix:499.99},
{modele:'iPhone 6', prix:699.99}];
}])
.filter('currencyFR', ['numberFilter', function(numberFilter){
return function(input, symbol){
if(isNaN(input)){ // on vérifie que l'entrée soit effectivement un nombre
return input; // sinon on renvoie tout simplement l'entrée
} else{
symbol = symbol || "€" ; // € par défaut, sinon, on récupère le symbole donné
input = numberFilter(input,2); // utilisation du filtre number pour afficher le montant
return input + ' ' + symbol; // on retourne la chaîne entière
}
};
}]);
Et la live-demo que vous pouvez modifier comme vous le souhaitez via Plunker :
Ici notre application customFilterExample
est excessivement simple. C'est pourquoi je n'ai pas pris la peine de créer différents modules. Cependant, lorsque vous créerez de vraies applications, il est vivement recommandé d'organiser vos éléments AngularJS. Par exemple, on créerait ici un module customFilterFilters
contenant tous nos filtres et dont dépenderait l'application principale customFilterExample
. Notre filtre currencyFR
serait donc intégré à ce sous-module.
Nous reverrons toutes ses subtilités dans la partie 3 où nous développerons une véritbale application.
Une fois votre filtre correctement codé et intégré à votre projet, vous pouvez vous en servir depuis n'importe quel fichier et à n'importe quel niveau. Que ce soit dans la vue — {{ donnees | monFiltre : param1 }}
— ou dans le JavaScript. Pour ce faire, vous n'avez qu'à procéder exactement de la même façon qu'avec les filtres natifs : en utilisant le service $filter
ou en l'invoquant avec le suffixe Filter
.
Comment utiliser les filtres ?
On peut faire appel à un filtre depuis le fichier correspondant au template de la page, pour rappel, c'est un fichier HTML (ex. :
partials/home.html
) ou depuis le code AngularJS directement (que ce soit un contrôleur, un service, une directive ou même un autre filtre).Dans un template
Pour utiliser un filtre depuis un template, il suffit de signaler à AngularJS que l'on va évaluer une expression avec les doubles accolades et d'y spécifier à l'intérieur tous les éléments nécessaires :
donnees
number
,String
,array
, ...)filtre
param1
,param2
, ...Si vous le souhaitez, il est tout à fait possible d'enchaîner des filtres dans une expression AngularJS. Il suffit de mettre les filtres à la suite les uns des autres avec leurs paramètres respectifs le cas échéant, en les séparant avec un
|
.{{ donnees | filtre1 : param1 : param2 | filtre2 | filtre3 : param1 : param2 }}
L'ordre d'exécution sera logiquement :
filtre1
puisfiltre2
puisfiltre3
Dans le code Javascript
Bien que ce cas d'utilisation soit plus rare, surtout quand on débute avec AngularJS, on peut tout à fait employer les filtres directement dans le code Javascript.
En reprenant les éléments vus plus haut, la syntaxe est la suivante (rien de sorcier) :
donneesFiltrees
Simplement, pour pouvoir utiliser un filtre, il faut bien sûr se placer dans un module AngularJS, que ce soit dans un contrôleur, une directive ou autre afin que le système d'injection de dépendances (Relire le paragraphe sur la Dependency Injection) puisse justement appeler le filtre demandé. Il y a alors 2 façons différentes de procéder :
Avec le service
$filter
En intégrant le service
$filter
dans les dépendances du module concerné, on peut ensuite utiliser n'importe quel filtre :En appelant directement le filtre concerné
Pour signaler qu'un module a besoin d'un filtre précis, on peut l'ajouter dans les dépendances mais en ajoutant au nom du filtre le suffixe
Filter
:Ces deux syntaxes fonctionnent parfaitement (même après minification si les dépendances sont bien signalées).
Évidemment, on utilisera la première syntaxe lorsqu'on a besoin de plusieurs filtres dans le même module plutôt que de tous les lister dans les dépendances. Si vous le souhaitez, vous pouvez utiliser la seconde syntaxe pour éviter d'appeler le service
$filter
lorsque vous ne faîtes appel qu'à un filtre précis mais ce n'est pas du tout obligatoire.Pour être sûr de ne pas se tromper, le plus simple semble encore d'utiliser toujours la même version, et donc de privilégier l'utilisation du service
$filter
.