Les filtres dans Angular, le petit truc sympa qui simplifie grandement la vie (je commence à comprendre de plus en plus pourquoi les développeurs java aiment Angular: tous des faignasses, moi aussi, donc j'aime Angular).
ng-repeat
Imaginons que je veuille ajouter au-dessus de ma liste de livre une zone de texte qui me permette de filtrer ma liste en fonction du texte saisi, et ceci de manière dynamique... et sans écrire une ligne de javascript :)
Actuellement notre liste ressemble à ceci:
<div class="uk-panel">
<h3 class="uk-panel-title">Liste des Livres</h3>
<table class="uk-table uk-table-hover">
<thead>
<tr>
<th>Titre</th>
<th>Description</th>
<th>Niveau</th>
<th>id</th>
</tr>
</thead>
<tbody ng-repeat="book in books" ng-click="selectBook(book)">
<tr>
<td>{{book.title}}</td>
<td>{{book.description}}</td>
<td>{{book.level}}</td>
<td>{{book._id}}</td>
</tr>
</tbody>
</table>
</div>
Nous allons modifier le code de la façon suivante:
<div class="uk-panel">
<h3 class="uk-panel-title">Liste des Livres</h3>
On ajoute la zone de texte de recherche et on lui affecte un modèle searchText
(pas besoin de le déclarer dans le code, la directive suffit):
<form class="uk-form">
<input ng-model="searchText" placeholder="chercher un livre">
</form>
Ensuite on ne change rien ...
<table class="uk-table uk-table-hover">
<thead>
<tr>
<th>Titre</th>
<th>Description</th>
<th>Niveau</th>
<th>id</th>
</tr>
</thead>
... sauf au niveau de la directive ng-repeat
à laquelle on ajoute un filtre sur la valeur de searchText
:
<tbody ng-repeat="book in books | filter:searchText" ng-click="selectBook(book)">
<tr>
<td>{{book.title}}</td>
<td>{{book.description}}</td>
<td>{{book.level}}</td>
<td>{{book._id}}</td>
</tr>
</tbody>
</table>
</div>
Nous avons donc ajouté 3 lignes, et modifié une 4ème. Faites l'exercice d'aller demander à un développeur "pas Angular", à combien de temps il chiffrerait cette fonctionnalité ;). (mais ça c'est un autre sujet ...)
Vous n'avez plus qu'à tester, ça fonctionne très bien mais par contre cela fait de la recherche sur l'ensemble des colonnes. Je voudrais pouvoir faire des recherches par champs.
Avant tout, allez modifier notre factory Models
, on ajoute un niveau ""
qui permettra de faire des recherches "tous niveaux" (ou autrement dit sans filtre sur le niveau):
booksApp.factory("Models", function() {
var levels = function() {
return [
"", "très bon", "bon", "débutant"
];
}
return {
levels : levels
}
});
Modifions le formulaire de recherche de la manière suivante:
<form class="uk-form">
<input ng-model="search.$" placeholder="chercher un livre">
<input ng-model="search.title" placeholder="par titre">
<input ng-model="search.description" placeholder="par description">
<select ng-model="search.level"
ng-options="level for level in levels">
</select>
</form>
Donc nous avons ajouté des champs de recherche et modifié les directives ng-model
:
ng-model="search.$"
: on cherche dans tous les champsng-model="search.title"
: on cherche dans le titreModifions ensuite la directive ng-repeat
pour prendre en compte les changements:
<tbody ng-repeat="book in books | filter:search:strict" ng-click="selectBook(book)">
Donc, pas beaucoup plus compliqué que toute à l'heure. Vous pouvez tester, et vous aller remarquer que les filtres de recherches sont cumulatifs! Donc une joli fonctionnalité de recherche à pas cher ;).
Vous pouvez aussi utiliser l'API filter
en "pur" javascript si vous devez filtrer des tableaux, mais là je vous laisse jeter un coup d'oeil à la documentation d'Angular https://docs.angularjs.org/api/ng/filter/filter.
Si vous faites des recherches dans Google sur angular + filter
, vous tomberez sur le sujet que nous venons de traiter mais aussi sur la notion de filtre de format d'affichage : on définit la manière dont on veut afficher les données (dates, nombre, monnaie, ...).
Par exemple, toujours dans ma liste, je souhaite afficher les niveaux en majuscules. Il suffit de modifier :
<td>{{book.level}}</td>
par :
<td>{{book.level | uppercase}}</td>
Là aussi je vous engage à aller voir la documentation : https://docs.angularjs.org/guide/filter
stars
Je souhaite afficher un certain nombre d'étoiles en fonction du niveau, plutôt que d'afficher le niveau en toutes lettres. Pour cela, il suffit de quelques lignes de javascript pour créer son propre filtre :
Allons éditer public/js/main.js
pour ajouter le code suivant:
booksApp.filter("stars", function() {
return function(data) {
switch (data) {
case "très bon":
return "***";
break;
case "bon":
return "**";
break;
case "débutant":
return "*";
break;
default:
return "";
}
}
});
Nous avons donc notre filtre, et pour l'utiliser il suffit de modifier côté html la ligne de toute à l'heure:
<td>{{book.level | uppercase}}</td>
en remplaçant le filtre uppercase
par stars
:
<td>{{book.level | stars}}</td>
Vous n'avez plus qu'à vérifier. Pratique non?