Apprendre Vue 3 : Directives

Gestion des évènements, conditions, boucles et binding : ces fonctions de templating sont regroupées dans ce que l'on appelle des directives.

Icône de calendrier
Intermédiaire
9 chapitres

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} :

copié !
<button v-on:click="counter++">Cliquez-moi</button>
<p>Le bouton a été cliqué {{ counter }} fois.</p>
copié !
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.

copié !
<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.

copié !
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 :

copié !
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.

copié !
<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}.

copié !
<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.

copié !
<h1 v-if="login">{{ username }}</h1>

v-else permet quant à lui d’afficher un bloc alternatif si la condition renvoie false.

copié !
<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.

copié !
<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 :

copié !
	<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.

copié !
<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
copié !
<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 :

copié !
<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} :

copié !
<ul>
	<li v-for="item in items">
		<h3>{{ item.title }}</h3>
		<p>{{ item.content }}</p>
	</li>
</ul>
copié !
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.

copié !
<ul>
	<li v-for="property in article">
		{{ property }}
	</li>
</ul>
copié !
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).

copié !
data() {
	return {
		numbers: [1, 2, 3, 4, 5]
	}
},
computed: {
	evenNumbers() {
		return this.numbers.filter(number => number % 2 === 0)
	}
}
copié !
<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é.

copié !
<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>.

copié !
<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 :

copié !
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 :

copié !
<img src="image.src" alt="image.alt" v-for="image in images" :key="image.id">

Ou encore… ca :

copié !
<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.

copié !
<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 :

copié !
data() {
	return {
		active: true,
		type: 'success'
	}
}
copié !
<span class="alert" :class="[{ 'active': active }, `alert-${type}`]">
	Mauvais mot de passe
</span>

Binder du style inline

Considérons l’état suivant :

copié !
data() {
	return {
		activeColor: 'red',
		fontSize: 24
	}
}
copié !
<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.

copié !
<input type="text" name="username" v-model="username">
copié !
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…

copié !
<input
	type="text"
	name="username"
	:value="username"
	@input="(event) => { username = event.target.value }">

En résumé 👇

ComportementSupport
One way data bindingLa 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