Formation Vue 3 | Dynamiser ses Templates avec les Directives
Gestion des évènements, conditions, boucles et binding : ces fonctions de templating sont regroupées dans ce que l'on appelle des directives.
Qu’est ce qu’une directive ?
Les directives nous fournissent un moyen de résoudre des problèmes courants de templating dynamique et de connectivité données/DOM.
Nous devons en grande partie le concept des directives à AngularJS, ancêtre d’Angular. Une directive est une sorte de marqueur que l’on place dans le balisage HTML via un attribut spécifique, indiquant à la bibliothèque (en l’occurrence Vue.js), de faire quelque chose à un élément du DOM.
Elles sont particulièrement puissantes et écrites de manière sémantique, afin que le code soit le plus explicite et logique possible. Elles ressemblent à des attributs HTML classiques, à la différence qu’elles sont préfixées par v-
.
Détaillons dans ce chapitre la liste des directives les plus couramment utilisées au sein des projets Vue.js.
Événements (v-on
)
Événement unique
Les événements sont au cœur de JavaScript, car c’est en partie grâce à eux que nos visiteurs pourront interagir avec notre interface (boutons et formulaires).
Placer un écouteur d’événement en Vue est similaire à l’approche consistant à placer un attribut HTML on{evenement}
directement au sein de la balise concernée, à la différence que cet attribut sera remplacé par une directive à la forme v-on:{evenement}
:
<button v-on:click="counter++">Cliquez-moi</button>
<p>Le bouton a été cliqué {{ counter }} fois.</p>
data() {
return {
counter: 0
}
}
Bien souvent, la logique que nous souhaitons mettre en place au déclenchement d’un événement est bien plus complexe qu’une simple incrémentation de compteur, comme dans l’exemple précédent.
Pour cela on aura la possibilité d’appeler une méthode directement depuis la directive v-on
.
<p>Acheter {{ article.name }} au prix de {{ article.price }}€. Combien en voulez-vous ?</p>
<select v-on:change="addToCart($event)">
<option value="1">1</option>
<option value="5">5 (- 10%)</option>
<option value="10">10 (- 25%)</option>
</select>
Ici, lorsque l’utilisateur sélectionne une option, la méthode addToCart()
est appelée, et l’objet $event
lui est transmis en argument. Cet argument permet d’accéder à l’objet event
de JavaScript contenant tout un tas d’attributs (clientX
, clientY
…) et méthodes (preventDefault()
…) utiles au traitement des événements.
data() {
return {
article: {
name: "DVD",
price: 20
}
}
},
methods: {
addToCart(event) {
const offers = {
1: 1,
5: 0.9,
10: 0.75
};
const number = event.target.value;
const total = number * this.article.price * offers[number];
alert(`Vous avez choisi ${number} ${this.article.name}(s) pour ${total}€`);
}
}
event.target.value
permet de récupérer la valeur de l’option sélectionnée. En fonction de cette valeur, un prix dégressif est appliqué.
Événements et scripts multiples
Considérons les 3 méthodes suivantes :
methods: {
method1(attr1, attr2, event) { ... },
method2(event) { ... },
method3() { ... }
}
Pour attacher plusieurs scripts à un même évènement, il suffit de préciser plusieurs méthodes, séparées par une virgule.
<button v-on:click="method1(attr1, attr2, $event), method2($event)">ok</button>
Pour attacher plusieurs évènements à un même élément, on spécifie plusieurs directives v-on:{event}
.
<button v-on:click="method1(attr1, attr2, $event)" v-on:dblclick="method3()">ok</button>
Rendu conditionnel
v-if
, v-else-if
et v-else
v-if
est utilisé pour afficher conditionnellement un bloc. À la manière d’une condition classique, le bloc ne sera rendu que si l’expression de la directive renvoie le booléen true
.
<h1 v-if="login">{{ username }}</h1>
v-else
permet quant à lui d’afficher un bloc alternatif si la condition renvoie false
.
<p v-if="login">Bonjour {{ username }}</p>
<p v-else>Bonjour</p>
Et comme tout n’est pas systématiquement binaire, v-else-if
permet de considérer plusieurs scénarios alternatifs.
<div v-if="bloc_num === 1">
BLOC 1
</div>
<div v-else-if="bloc_num === 2">
BLOC 2
</div>
<div v-else-if="bloc_num === 3">
BLOC 3
</div>
<div v-else>
BLOC PAR DEFAUT
</div>
Les groupes conditionnels
Lorsque nous souhaitons afficher de manière conditionnelle un groupe d’éléments de même niveau, on aurait tendance à démultiplier les directives v-if
:
<h1 v-if="ok">Title</h1>
<p v-if="ok">Paragraph 1</p>
<p v-if="ok">Paragraph 2</p>
Mais soyons d’accord, côté template c’est pas jojo…
On pourra alors utiliser la directive v-if
sur un élément <template>
, qui sert d’enveloppe invisible.
<template v-if="ok">
<h1>Title</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</template>
Le rendu final n’inclura pas la balise <template>
.
v-show
La directive v-show
semble similaire à la directive v-if
dans la mesure où elle va afficher un élément si une condition est vérifiée. En revanche, ces deux directives sont bien distinctes :
v-show
permute la visibilité de l’élément via du CSS (display: none
)v-if
supprime complètement l’élément du DOM
<button @click="showModal = !showModal">En savoir plus</button>
<div v-show="showModal" class="modal">...</div>
v-show
est généralement utilisée dans le but de contrôler la visibilité d’un élément faisant l’objet de permutations fréquentes (toggle).
Rendu itératif (v-for
)
Que serait un site web dynamique sans les boucles ? Pas grand-chose… qu’il s’agisse d’afficher des articles, des produits, des utilisateurs, des posts, des messages, etc., les boucles sont essentielles.
La directive v-for
permet de boucler sur tout un tas de choses. Sa syntaxe à la forme d’une boucle for...in
classique.
La plupart du temps cette boucle s’exploite sur un tableau d’objets (rendu par une API), mais pas toujours…
Boucler sur des nombres
Cette boucle peut aussi être utilisée de manière à itérer sur des nombres :
<div class="demo">
<span v-for="i in 10">{{ i }}</span>
</div>
Boucler sur un tableau
La directive v-for
a pour valeur {alias de l'élément courant} in {data source}
:
<ul>
<li v-for="item in items">
<h3>{{ item.title }}</h3>
<p>{{ item.content }}</p>
</li>
</ul>
data() {
return {
items: [
{ title: 'T1', content: '...' },
{ title: 'T2', content: '...' }
]
}
}
v-for
accepte également un deuxième argument facultatif pour l’index de l’élément courant. Par exemple : v-for="(item, index) in items"
Boucler sur un objet
Si v-for
est utilisée sur un objet simple, alors l’alias de boucle itèrera sur l’ensemble de ses propriétés.
<ul>
<li v-for="property in article">
{{ property }}
</li>
</ul>
data() {
return {
article: {
title: 'Apprendre Vue.js',
content: 'Blablabla'
}
}
}
Boucler sur un résultat filtré / trié
v-for
permet également de boucler sur un résultat filtré retourné par une fonction (déclarée dans computed
ou methods
).
data() {
return {
numbers: [1, 2, 3, 4, 5]
}
},
computed: {
evenNumbers() {
return this.numbers.filter(number => number % 2 === 0)
}
}
<ul>
<li v-for="n in evenNumbers">
{{ n }}
</li>
</ul>
Une des bonnes pratiques officielle de Vue.js consiste à toujours ajouter une clé :key
sur l’élément qui porte une directive v-for
.
Cette clé joue le rôle d’identifiant unique du nœud HTML, permettant à Vue.js de suivre les éléments d’une liste qui ont été modifiés, ajoutés ou supprimés dynamiquement.
On dit qu’on « bind » une clé.
<ul>
<li v-for="item in items" :key="item.id">...</li>
</ul>
Les groupes itératifs
Tout comme pour une directive conditionnelle, une directive itérative ne peut s’attacher qu’à un unique élément. Vient donc un problème lorsque nous souhaitons boucler sur un groupe d’éléments de même niveau.
Dans ce cas, on utilise v-for
sur un élément <template>
, qui sert d’enveloppe invisible. Le rendu final n’inclura pas la balise <template>
.
<ul>
<template v-for="item in items">
<li>{{ item.title }}</li>
<li class="divider" role="presentation"></li>
</template>
</ul>
Binding (v-bind
)
Binder un attribut
Considérons l’état suivant :
data() {
return {
images: [
{ id: 1, alt: "Un chat dans un arbre", src: "uploads/cat.png" },
{ id: 2, alt: "Un ciel étoilé", src: "uploads/sky.png" },
{ id: 3, alt: "Des enfants qui jouent au foot", src: "uploads/childrens.png" }
]
}
},
Si vous souhaitez injecter des données dynamiquement au sein d’attributs HTML, on pourrait avoir tendance à écrire cela :
<img src="image.src" alt="image.alt" v-for="image in images" :key="image.id">
Ou encore… ca :
<img src="{{ image.src }}" alt="{{ image.alt}}" v-for="image in images" :key="image.id">
La bonne approche consiste en réalité à utiliser la directive v-bind:{attribut}="data"
. v-bind
permet de lier dynamiquement les valeurs des attributs HTML à des données ou des expressions JavaScript.
<img v-bind:src="image.src" v-bind:alt="image.alt" v-for="image in images" :key="image.id">
Cette directive est également très utilisée pour le binding de classes HTML ou de style CSS inline.
Binder des classes
Considérons l’état suivant :
data() {
return {
active: true,
type: 'success'
}
}
<span class="alert" :class="[{ 'active': active }, `alert-${type}`]">
Mauvais mot de passe
</span>
Binder du style inline
Considérons l’état suivant :
data() {
return {
activeColor: 'red',
fontSize: 24
}
}
<div :style="{ color: activeColor, fontSize: fontSize + 'px' }">...</div>
Liaison avec les formulaires (v-model
)
Avec Vue.js la réactivité implique que la modification d’une propriété réactive entraine son actualisation dans l’interface, mais pas l’inverse. On parle de “one way data binding”.
La directive v-model
permet de faire du “two way data binding”. Autrement dit des liaisons bidirectionnelles entre des champs de formulaires et l’état de notre application.
Cette directive permet de mettre à jour automatiquement une propriété réactive lorsque l’utilisateur interagit avec un champ de formulaire.
<input type="text" name="username" v-model="username">
data() {
return {
username: ''
}
}
v-model
: une histoire de sucre syntaxique
v-model
est ce que l’on appelle du sucre syntaxique.
Nous pourrions en réalité tout à fait nous passer de cette directive et utiliser à la place un évènement @input
combiné à un script qui modifie l’état. Mais cela serait dommage…
<input
type="text"
name="username"
:value="username"
@input="(event) => { username = event.target.value }">
En résumé 👇
Comportement | Support | |
---|---|---|
One way data binding | La mise à jour d’une propriété réactive engendre une actualisation de sa valeur dans le template. | Par défaut |
Two way data binding | - La mise à jour d’une propriété réactive engendre une actualisation de sa valeur dans le template. - La mise à jour d’une propriété réactive dans le template (via un champ de formulaire) entraine la modification de l’état. | Avec v-model |