Nous avons bien avancé dans l'apprentissage de vue.js. Nous avons comment établir des liaisons, parfois à double sens, entre la vue et le modèle. Jusque là nous n'avons effectué aucun traitement intermédiaire et les données du modèle se retrouvent telles qu'elles dans la vue, et réciproquement. Parfois nous avons besoin d'effectuer quelques modifications, soit avant affichage, soit avant mémorisation des données. Cet article est destiné à présenter cet aspect de vue.js.
Les filtres
Dans vue.js un filtre est une fonction qui transforme une donnée :
On a une donnée à l'entrée, elle est transformée et on la retrouve à la sortie dans un nouvel état.
Un simple filtre
Voici un exemple simple :
<div id="tuto"> La donnée est "{{ texte }}" mais elle est transformée en "{{ texte | capitalize }}". </div>
Et le Javascript :
new Vue({ el: '#tuto', data: { texte: 'coucou' } });
Avec comme résultat :
La donnée est "coucou" mais elle est transformée en "Coucou".
Pour ajouter un filtre on utilise la signe "|" suivi du nom du filtre. Ici capitalize
met en majuscule la première lettre.
Voici une illustration du fonctionnement :
Double filtrage
On peut cumuler les filtres :
<div id="tuto"> La donnée est "{{ texte }}" mais elle est transformée en "{{ texte | lowercase | capitalize }}". </div>
Avec ce Javascript :
new Vue({ el: '#tuto', data: { texte: 'COUCOU' } });
On obtient :
La donnée est "COUCOU" mais elle est transformée en "Coucou".
Pour mettre en œuvre plusieurs filtres il suffit de les ajouter, toujours avec le signe "|". Ici on commence par tout mettre en minuscules avec le filtre lowercase
, puis on met en majuscule le premier caractère avec capitalize
.
Voici une illustration du fonctionnement :
Le pluriel
On a souvent l'occasion de jouer avec des pluriels lorsqu'on affiche des données. En général on se retrouve à effectuer un test de quantité pour savoir s'il faut ajouter ou non un "s" à la fin d'un mot. Vue.js propose un filtre spécifique à ce problème :
<ul id="tuto"> <li v-for="animal in animaux"> {{ animal.nom }} : {{ animal.nombre }} {{ animal.nombre | pluralize 'présent' }} </li> </ul>
Avec ce Javascript :
new Vue({ el: '#tuto', data: { animaux: [ { nom: "Chat", nombre: 1 }, { nom: "Chien", nombre: 3 }, { nom: "Souris", nombre: 1 } ] } });
Le filtre pluralize
met le mot qu'on lui passe comme argument, ici "présent", au pluriel en lui ajoutant un "s" selon la valeur filtrée, ici "nombre". Donc quand "nombre" est plus grand que 1 il ajoute un "s" à "présent".
Vous allez dire que bien souvent dans la langue française on n'a pas un "s" pour mettre au pluriel. Dans ce cas on peut utiliser plusieurs arguments. Voici un exemple :
<ul id="tuto"> <li v-for="animal in animaux"> {{ animal.nom }} : {{ animal.nombre }} {{ animal.nombre | pluralize 'animal' 'animaux' }} </li> </ul>
Avec ce Javascript :
new Vue({ el: '#tuto', data: { animaux: [ { nom: "Chat", nombre: 1 }, { nom: "Chien", nombre: 3 }, { nom: "Souris", nombre: 2 } ] } });
Et ce résultat :
Le clavier
On a vu des cas de gestion d'un événement de la souris. On peut de la même manière gérer un événement du clavier. Mais dans ce cas on a pratiquement toujours besoin de savoir quelle touche du clavier a été utilisée. C'est ici qu'intervient le filtre key
.
Considérez ce cas :
<div id="tuto"> <input v-on:keypress="action"> </div>
On a une zone de texte et on écoute l'événement keypress
. Voici le Javascript pour traiter cet événement :
new Vue({ el: '#tuto', methods: { action: function() { alert('On a actionné une touche !') } } });
Lorsque le focus est placé dans la zone de texte, quelle que soit la touche actionnée on obtient l'affichage de la petite fenêtre d'information :
Pour prendre compte seulement une certaine touche on doit utiliser le filtre key
:
<input v-on:keypress.left="action">
On change le texte de l'alerte :
new Vue({ el: '#tuto', methods: { action: function() { alert('On a actionné la touche "flèche à gauche" !') } } });
Maintenant on aura la fenêtre que si on actionne la touche "flèche à gauche" :
Voici une schématisation du fonctionnement :
On obtiendrait le même résultat en transmettant le code de la touche :
<input v-on:keypress.37="action">
Vue.js possède des alias pour les touches les plus utilisées :
-
enter
-
tab
-
delete
-
esc
-
space
-
up
-
down
-
left
-
right
Vous avez donc le choix d'utiliser ces alias ou directement le code.
Filtrer une liste
On a aussi souvent besoin de filtrer des éléments d'une liste. Vue.js possède le filtre filterBy
pour le faire. Voici un exemple :
<ul id="tuto"> <li v-for="personne in personnes | filterBy 'actif'"> {{ personne.nom }} </li> </ul>
Et le Javascript :
new Vue({ el: '#tuto', data: { personnes: [ { nom: "Claret", statut: "actif" }, { nom: "Dupont", statut: "actif" }, { nom: "Durand", statut: "passif" }, { nom: "Martin", statut: "actif" }, { nom: "Torlet", statut: "passif" } ] } });
Avec ce rendu :
Les personnes ne sont prises en compte que si on trouve "actif" parmi les valeurs.
Mais que se passe-t-il si l'une des personnes s'appelle "Actif" :
personnes: [ { nom: "Claret", statut: "actif" }, { nom: "Dupont", statut: "actif" }, { nom: "Actif", statut: "passif" }, { nom: "Martin", statut: "actif" }, { nom: "Torlet", statut: "passif" } ]
Dans ce cas on va le retrouver dans le rendu alors que son statut est "passif" :
Dans ce cas on peut préciser sur quelle propriété on veut effectuer le filtrage :
<li v-for="personne in personnes | filterBy 'actif' in 'statut'">
Cette fois monsieur Actif ne sera pas sélectionné !
Voici une schématisation :
Évidemment la valeur de filtrage peut être dans une variable et on peut ainsi envisager des choses intéressantes comme nous le verrons dans l'exemple de fin.
Un peu d'ordre
Lorsqu'on crée à l'affichage une liste avec la directive v-for
les données sont prises en compte dans l'ordre du modèle. Si on veut modifier cet ordre, par exemple avoir un ordre alphabétique, on peut évidemment réordonner ces données dans le modèle, mais on peut aussi utiliser le filtre orderBy
.
Voici un exemple :
<ul id="tuto"> <li v-for="personne in personnes | orderBy 'nom'"> {{ personne.nom }} </li> </ul>
Ici on a ajouté le filtre orderBy
en précisant qu'il doit agir sur la propriété "nom".
Avec ce Javascript :
new Vue({ el: '#tuto', data: { personnes: [ { nom: "Martin"}, { nom: "Claret"}, { nom: "Durand"}, { nom: "Dupont"}, { nom: "Torlet"} ] } });
On voit que les noms ne sont pas dans l'ordre alphabétique au niveau du modèle. Pourtant on obtient grâce au filtre ce résultat :
Voici une schématisation :
On peut obtenir l'ordre inverse en ajoutant un deuxième argument qui doit être négatif :
<li v-for="personne in personnes | orderBy 'nom' -1">
Si on met -1
comme ici alors l'ordre est inversé :
Si on prévoit une valeur positive on aura l'ordre normal, donc autant dans ce cas ne rien mettre !
On peut aussi prévoir de gérer dynamiquement l'ordre de sortie avec une variable.
Un exemple
Voyons à présent un exemple récapitulatif pour voir les filtres en action dans une situation réaliste. On va reprendre le cas d'un tableau de personnes mais cette fois on va gérer l'ordre alphabétique normal ou inverse par colonne et on va ajouter la possibilité de filtrer les lignes. Voici le code complet de l'exemple :
<!DOCTYPE html> <html lang="fr"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Test vue.js</title> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css"> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css"> </head> <body> <div class="container" id="tuto"> <br> <div class="panel panel-primary"> <div class="panel-heading">Personnes</div> <table class="table table-bordered table-striped"> <thead> <tr> <th v-for="head in heads"> {{ head.titre }} <a href="#" v-on:click="action($index)"> <span class="fa fa-fw fa-sort{{ head.classe }}"></span> </a> </th> </tr> </thead> <tbody> <tr v-for=" personne in personnes | filterBy search in filterKey | orderBy sortKey orderKey"> <td>{{ personne.nom | uppercase }}</td> <td>{{ personne.prenom }}</td> <td>{{ personne.statut }}</td> </tr> </tbody> </table> </div> <form> <div class="form-group"> <input type="text" class="form-control" placeholder="Recherche ici" v-model="search"> </div> <div class="radio" v-for="head in heads"> <label> <input type="radio" value="{{ head.sort }}" v-model="filterKey">{{ head.titre }} </label> </div> </form> </div> <script src="http://cdn.jsdelivr.net/vue/1.0.10/vue.min.js"></script> <script> var classOrder = ['', '-desc', '-asc']; var keyOrder = ['', 1, -1]; new Vue({ el: '#tuto', data: { personnes: [ { nom: "Claret", prenom: "Marcel", statut: 'Administrateur' }, { nom: "Durand", prenom: "Jacques", statut: 'Utilisateur' }, { nom: "Dupont", prenom: "Albert", statut: 'Utilisateur' }, { nom: "Torlet", prenom: "Arthur", statut: 'Rédacteur' }, { nom: "Martin", prenom: "Denis", statut: 'Utilisateur' } ], heads: [ { titre: 'Nom', classe: '', order: 0, sort: 'nom' }, { titre: 'Prénom', classe: '', order: 0, sort: 'prenom' }, { titre: 'Statut', classe: '', order: 0, sort: 'statut' } ], sortKey: '', orderKey: '', search: '', filterKey: 'nom' }, methods: { action: function(index) { var self = this; this.heads.forEach(function(el) { if(el.sort != self.heads[index].sort) { el.order = 0; el.classe = ''; }; }); var info = this.heads[index]; if(++info.order == 3) info.order = 1; info.classe = classOrder[info.order]; this.heads.$set(index, info) this.orderKey = keyOrder[info.order]; this.sortKey = info.sort; } } }); </script> </body> </html>
Fonctionnement
Au départ on a cet aspect :
On a 3 colonnes pour les noms, les prénoms et le statut. Dans la partie inférieure une zone de texte permet de filtrer la liste et on peut choisir entre les colonnes pour ce filtrage. Il est prévu des petites icônes pour figurer le tri. Au départ aucun tri n'étant effectué on a les deux petits triangles. Si on clique dessus on ordonne selon la colonne, par exemple pour les prénoms :
La petite icône change pour figurer le tri décroissant. Si on clique à nouveau on passe en mode croissant et la petite icône change encore :
Si maintenant on clique sur l'icône de la colonne des noms on remet au repos celle des prénoms et le tri se fait alors sur la colonne des noms :
Et ainsi de suite pour chaque colonne.
Pour le filtrage c'est plus simple : on choisit la colonne avec les boutons radio et on tape les caractères désirés :
Remplissage du tableau
Toutes les données pour remplir le tableau se trouvent dans le modèle :
data: { personnes: [ { nom: "Claret", prenom: "Marcel", statut: 'Administrateur' }, { nom: "Durand", prenom: "Jacques", statut: 'Utilisateur' }, { nom: "Dupont", prenom: "Albert", statut: 'Utilisateur' }, { nom: "Torlet", prenom: "Arthur", statut: 'Rédacteur' }, { nom: "Martin", prenom: "Denis", statut: 'Utilisateur' } ], heads: [ { titre: 'Nom', classe: '', order: 0, sort: 'nom' }, { titre: 'Prénom', classe: '', order: 0, sort: 'prenom' }, { titre: 'Statut', classe: '', order: 0, sort: 'statut' } ],
Un tableau d'objets pour les lignes et un autre pour les titres des colonnes et d'autres informations que nous allons voir plus loin.
L'entête du tableau est remplie avec ce code :
<th v-for="head in heads"> {{ head.titre }} <a href="#" v-on:click="action($index)"> <span class="fa fa-fw fa-sort{{ head.classe }}"></span> </a> </th>
On utilise la directive v-for
sur la propriété heads
. On utilise mustache pour insérer le titre. on prévoit ensuite un lien pour l'icône de tri avec une directive v-on
pour gérer l'événement en appelant la méthode action
avec l'index comme paramètre. On gère le changement de l'aspect de l'icône en jouant sur la classe avec la propriété classe
.
Le corps du tableau est rempli avec ce code :
<tr v-for=" personne in personnes | filterBy search in filterKey | orderBy sortKey orderKey"> <td>{{ personne.nom | uppercase }}</td> <td>{{ personne.prenom }}</td> <td>{{ personne.statut }}</td> </tr>
On utilise aussi la directive v-for
sur la propriété personnes
. Nous verrons les filtres plus loin. Les données des lignes suivent dans les balises td
. On remarque l'utilisation du filtre uppercase
pour avoir les noms en capitales.
Le tri
Le tri est assuré par le filtre orderBy
:
orderBy sortKey orderKey
On retrouve les deux arguments dans le modèle :
data: { ... sortKey: '', orderKey: '', ... },
Il suffit donc de bien renseigner ces propriétés pour assurer le tri. On a vu plus haut qu'on appelle la méthode action
avec comme paramètre l'index de la colonne lorsqu'on clique sur l'icône de tri. Voici cette méthode :
action: function(index) { var self = this; this.heads.forEach(function(el) { if(el.sort != self.heads[index].sort) { el.order = 0; el.classe = ''; }; }); var info = this.heads[index]; if(++info.order == 3) info.order = 1; info.classe = classOrder[info.order]; this.heads.$set(index, info) this.orderKey = keyOrder[info.order]; this.sortKey = info.sort; }
Cette méthode accomplit un certain nombre d'actions :
-
réinitialise les propriétés
order
etclasse
de toutes les autres colonnes pour le cas où on a déjà un tri actif -
incrémente la propriété
order
de la colonne et la ramène dans l'intervalle 1-2 -
actualise les données du modèle
-
assigne la nouvelle valeur des propriétés
orderKey
etsortKey
pour l'affichage
Filtrage
Le filtrage est plus simple, on s'en sort élégamment seulement avec les liaisons.
Dans le modèle on a :
data: { ... search: '', filterKey: 'nom' },
La propriété search
est liée à la zone de texte :
<input type="text" class="form-control" placeholder="Recherche ici" v-model="search">
Le filtrage est assuré par le filtre filterBy
:
filterBy search in filterKey
On change la colonne de filtrage avec les boutons radio :
<div class="radio" v-for="head in heads"> <label> <input type="radio" value="{{ head.sort }}" v-model="filterKey">{{ head.titre }} </label> </div>
On crée ces boutons radio avec encore la directive v-for
qui aide à rendre le HTML plus lisible.
Cet exemple aurait pu être codé de façon différente mais en l'état il constitue un bon récapitulatif de ce que nous avons vu jusque là.
En résumé
-
Les filtres permettent de modifier les données avant leur affichage.
-
On peut utiliser plusieurs filtres.
-
Il existe un filtre spécifique pour les événements du clavier.
-
Le filtre
filterBy
permet de filtrer une liste. -
Le filtre
orderBy
permet d'ordonner une liste.
Par bestmomo
Aucun commentaire