Gestion des formulaires
Introduction
Les formulaires occupent une place centrale dans les applications web, qu'il s'agisse de se connecter à un compte, de passer une commande ou encore de lancer une recherche. Ils permettent à l'utilisateur de fournir des informations que l'application doit ensuite exploiter. Avec Vue, il est possible de relier directement un champ de formulaire à une donnée JavaScript afin que toute modification de l'un se répercute automatiquement sur l'autre. Cette approche évite d'écrire du code répétitif et garantit une interface plus fluide.
La directive v-model
La directive v-model met en place une liaison réactive à double sens entre un champ de formulaire et une donnée JavaScript :
- au chargement, le champ affiche la valeur actuelle de la donnée réactive ;
- quand l'utilisateur modifie le champ, la donnée est mise à jour automatiquement ;
- si la donnée change dans le code, le champ se met à jour côté interface.
v-model fonctionne avec la plupart des champs standards et chaque type a son comportement spécifique. Par exemple, Les champs texte et radio renvoient une chaîne de caractères, une seule case à cocher renvoie un booléen, plusieurs cases à cocher renvoient un tableau, une liste à choix unique peut renvoyer une chaîne de caractères ou un tableau en mode multiple, etc.
<script setup>
import { ref } from 'vue'
const prenom = ref('')
// Petite fonction pour démontrer la synchronisation dans l'autre sens.
const appliquerPrenomParDefaut = () =>
{
prenom.value = 'Alice'
}
</script>
<template>
<label for="prenom">Prénom</label>
<input id="prenom" type="text" v-model="prenom" placeholder="Entrez votre prénom">
<p>Valeur de la données réactive "prenom" : {{ prenom }}</p>
<!-- En cliquant, on modifie la donnée réactive depuis le code -->
<button type="button" @click="appliquerPrenomParDefaut">Appliquer prénom par défaut</button>
</template>
Le champ et la variable prenom sont maintenant synchronisés automatiquement
- Au démarrage, la donnée réactive prenom est initialisée avec une chaîne de caractères vide (ref('')). Le champ "prénom" du formulaire reçoit cette valeur et l'affiche.
- Lorsque l'utilisateur saisit des caractères dans le champ "prénom", prenom.value se met à jour en temps réel avec cette saisie.
- Lorsque la fonction appliquerPrenomParDefaut() est appelée, prenom.value devient "alice" et le champ "prénom" s'actualise immédiatement pour afficher cette valeur.
Champ texte et zone de texte
On a déjà vu le principe avec un simple champ texte. Une zone de texte (<textarea>) fonctionne de la même manière, on lie la donnée avec v-model.
<script setup>
import { ref } from 'vue'
const sujet = ref('')
const message = ref('')
</script>
<template>
<label for="sujet">Sujet</label>
<input id="sujet" type="text" v-model="sujet" placeholder="Ex. Demande de devis">
<label for="message">Message</label>
<textarea id="message" v-model="message" rows="4" placeholder="Décrivez votre besoin"></textarea>
<p>Aperçu : {{ sujet }} — {{ message.length }} caractères</p>
</template>
La saisie met à jour les deux données réactives en direct et l'interface reflète ces valeurs sans code supplémentaire.
Boutons radio
Un groupe de boutons radio permet de choisir une option parmi plusieurs. La donnée liée contient la valeur de l'option sélectionnée.
<script setup>
import { ref } from 'vue'
const modeLivraison = ref('standard')
</script>
<template>
<fieldset>
<legend>Mode de livraison</legend>
<label>
<input type="radio" value="standard" v-model="modeLivraison">
Standard (3 jours)
</label>
<label>
<input type="radio" value="express" v-model="modeLivraison">
Express (24 h)
</label>
</fieldset>
<p>Choix : {{ modeLivraison }}</p>
</template>
Les deux boutons partagent la même donnée. Une seule valeur est possible à la fois et modeLivraison reçoit la valeur du bouton coché.
Case à cocher
Case à cocher unique
Une case à cocher seule représente un booléen. La donnée liée avec v-model vaut true quand la case est cochée et false quand elle est décochée.
<script setup>
import { ref } from 'vue'
const accepterConditions = ref(false)
const basculerParCode = () =>
{
accepterConditions.value = !accepterConditions.value
}
</script>
<template>
<fieldset>
<legend>Conditions générales</legend>
<label>
<input type="checkbox" v-model="accepterConditions">
J'accepte les conditions générales
</label>
<p>État actuel {{ accepterConditions ? 'accepté' : 'non accepté' }}</p>
<button type="button" @click="basculerParCode">Basculer par code</button>
</fieldset>
</template>
La case met à jour la donnée en temps réel et toute modification depuis le code s'affiche immédiatement. Ici, le bouton démontre la synchronisation dans l'autre sens.
Variante avec valeurs métier
Si l'on préfère stocker des valeurs ayant un sens pour l'application plutôt qu'un simple booléen, on peut utiliser :true-value et :false-value.
<script setup>
import { ref } from 'vue'
// La constante 'statut' peut avoir comme valeur "accepted" ou "not_accepted".
const statut = ref('not_accepted')
</script>
<template>
<label>
<input
type="checkbox"
v-model="statut"
:true-value="'accepted'"
:false-value="'not_accepted'"
>
Accepter la charte
</label>
<p>Statut {{ statut }}</p>
</template>
Cette approche évite les conversions manuelles et reste entièrement réactive.
Cases à cocher multiples
Un groupe de cases à cocher alimente un tableau. La donnée liée contient la liste des valeurs cochées.
<script setup>
import { ref } from 'vue'
// Tableau des valeurs cochées.
const accessoires = ref([])
</script>
<template>
<fieldset>
<legend>Accessoires</legend>
<label>
<input type="checkbox" value="housse" v-model="accessoires">
Housse de protection
</label>
<label>
<input type="checkbox" value="garantie" v-model="accessoires">
Extension de garantie
</label>
<label>
<input type="checkbox" value="chargeur" v-model="accessoires">
Chargeur supplémentaire
</label>
</fieldset>
<p>Accessoires sélectionnés : {{ accessoires || 'aucun' }}</p>
</template>
Cocher ajoute la valeur dans le tableau. Décocher la retire. L'ordre d'affichage est indépendant de l'ordre dans le tableau.
Listes déroulantes
Une liste simple renvoie une valeur unique.
<script setup>
import { ref } from 'vue'
// Donnée de référence liée à la balise "select".
const pays = ref('be')
</script>
<template>
<label for="pays">Pays</label>
<select id="pays" v-model="pays">
<option value="be">Belgique</option>
<option value="fr">France</option>
<option value="nl">Pays-Bas</option>
</select>
<p>Pays sélectionné : {{ pays }}</p>
</template>
Une liste en mode multiple renvoie un tableau des valeurs sélectionnées.
<script setup>
import { ref } from 'vue'
// Donnée de référence liée à la balise "select".
const tags = ref([])
</script>
<template>
<label for="tags">Tags</label>
<!-- L'attribut mutliple permet de sélectionner plusieurs options. -->
<select id="tags" multiple v-model="tags">
<option value="audio">Audio</option>
<option value="clavier">Clavier</option>
<option value="souris">Souris</option>
</select>
<p>Tag(s) sélectionné(s) : {{ tags }}</p>
</template>
Modificateurs utiles
Les modificateurs de v-model permettent d'ajuster la valeur saisie avant qu'elle ne soit stockée dans la donnée réactive :
- v-model.number tente de convertir la valeur en nombre au moment de la mise à jour. Si la saisie n'est pas un nombre valide, la valeur reste une chaîne. Utile pour des quantités, montants, identifiants numériques. Attention, les virgules décimales locales (ex. "1,23") ne sont pas interprétées par défaut.
- v-model.trim supprime les espaces en début et en fin de chaîne (les espaces internes sont conservés). Pratique pour des emails, codes promo, champs de recherche ou tout champ sensible aux espaces parasites.
- v-model.lazy met à jour la donnée lors de l'événement change (généralement au blur (perte de focus du champ) ou à la validation par Entrée), plutôt qu'à chaque frappe. Idéal quand la mise à jour déclenche une validation coûteuse, un appel API ou toute action qu'on ne veut pas répéter à chaque caractère.
<script setup>
import { ref } from 'vue'
const quantite = ref(1)
const recherche = ref('')
const email = ref('')
</script>
<template>
<label>Quantité</label>
<input type="text" v-model.number="quantite">
<label>Recherche</label>
<input type="search" v-model.trim="recherche" placeholder="Tapez un mot-clé">
<label>Email</label>
<input type="email" v-model.lazy="email" placeholder="votre@mail.com">
<ul>
<li>quantite : {{ quantite }} (de type "{{ typeof quantite }}")</li>
<li>recherche : {{ recherche }}</li>
<li>email : {{ email }}</li>
</ul>
</template>
Notez que les modificateurs peuvent se combiner selon le besoin, par exemple v-model.trim.number.
Exercices
Créer un nouveau projet Vue nommé vuejs04-gestion-des-formulaires en suivant la procédure décrite dans le chapitre dédié. Tous les exercices de cette page peuvent être réalisés dans ce projet, directement les uns à la suite des autres dans le fichier App.vue.
Dans la partie template, créer une balise <h1> avec le texte suivante : "Vue.js 04 : Gestion des formulaires".
Exercice 01 : Introduire v-model sur un champ texte
Objectif : Lier un champ texte à une donnée réactive et afficher sa valeur mise à jour.
Instructions :
- Dans la partie <script>, importer la fonction ref depuis Vue.
- Déclarer une constante réactive nommée titre initialisée à une chaîne vide via ref().
- Dans la partie <template>, ajouter un sous-titre <h2> avec le texte "Exo 01".
- Ajouter un champ texte et lui définir un placeholder explicite (ex. : "Titre de la demande").
- Lier le champ texte à la constante titre avec la directive v-model.
- Ajouter un paragraphe d'aperçu qui affiche : "Titre : " suivi du contenu de la constante titre.
- Tester : saisir du texte dans le champ doit mettre à jour titre via v-model. Comme titre est une constante réactive créée avec ref(), son affichage dans le <template> se met à jour automatiquement.
Exercice 02 : Basculer un statut avec une case à cocher
Objectif : Utiliser une <input type="checkbox"> liée avec v-model pour piloter une donnée booléenne.
Instructions :
- Dans la partie <script>, importer ref depuis Vue.
- Créer une constante réactive nommée actif initialisée à false via ref().
- Dans la partie <template>, ajouter un sous-titre <h2> avec le texte "Exo 02".
- Créer un <fieldset> avec le titre "Visibilité du produit" placé dans une balise <legend>.
- Sous la balise <legend>, créer une balise <label>.
- Dans la balise <label>, créer une case à cocher (checkbox) et la lier à la constante réactive actif avec la directive v-model.
- Ajouter le texte "Rendre le produit visible en ligne" sous la case à cocher (au sein de la balise <label>).
- Ajouter un paragraphe d'aperçu affichant : "Statut : " suivi d'un ternaire dans les moustaches basé sur la valeur de la constante actif (ex. : "actif" / "inactif").
- Tester : cocher/décocher la case met à jour immédiatement la constante réactive actif qui met automatiquement le texte "Statut : ..." à jour.
Exercice 03 : Sélectionner plusieurs options avec des cases à cocher
Objectif : Alimenter un tableau réactif avec un groupe de cases à cocher (ex. : permissions d'un compte).
Instructions :
- Dans la partie <script>, importer ref depuis Vue.
- Créer une constante réactive nommée permissions initialisée à un tableau vide via ref().
- Dans la partie <template>, ajouter un sous-titre <h2> avec le texte "Exo 03".
- Créer un <fieldset> avec le titre "Permissions" placé dans une balise <legend>.
- Sous la balise <legend>, créer une première balise <label>.
- Dans cette balise <label>, créer une case à cocher (checkbox) et la lier à la constante réactive permissions avec la directive v-model. Donner à cette case la valeur "lecture" à l'aide de l'attribut HTML value et ajouter le texte "Lecture" au <label>.
- Créer une deuxième balise <label> basée sur le même modèle précédent mais cette fois-ci avec la valeur "lecture" et le texte "Écriture".
- Créer une troisième balise <label> basée sur le même modèle précédent mais cette fois-ci avec la valeur "admin" et le texte "Administration".
- Ajouter un paragraphe d'aperçu affichant "Nombre sélectionné : " suivi du nombre d'élément présent dans le tableau permissions.
- Ajouter un paragraphe affichant "Permissions : " puis, successivement, les trois premières cases du tableau.
- Tester : cocher/décocher chaque case doit ajouter/retirer sa valeur dans le tableau permissions et mettre à jour l'aperçu immédiatement. (Ne pas utiliser .prevent dans ce cas.)
Exercice 04 : Choisir le statut d'un ticket avec une liste déroulante
Objectif : Lier un <select> à une propriété d'un objet réactif pour mettre à jour le statut d'un ticket.
Instructions :
- Dans la partie <script>, importer la fonction reactive depuis Vue.
-
Créer une constante réactive nommée ticket avec
reactive(), contenant les propriétés suivantes :
- titre : "Problème d'accès".
- statut : "brouillon".
- Dans la partie <template>, ajouter un sous-titre <h2> avec le texte "Exo 04".
- Ajouter un libellé (<label>) pour la liste déroulante. Lui ajouter le texte "Statut" et le lier (avec l'attribut HTML for) à l'identifiant nommé "statut" de la future liste déroulante.
- Sous le libellé, créer la liste déroulante <select> avec l'attribut HTML id dont la valeur vaut "statut" et lier la liste à la propriété statut de l'objet réactif ticket à l'aide de la directive v-model.
- Créer une première balise option dans la balise select. Donner à cette option la valeur "brouillon" à l'aide de l'attribut HTML value et lui ajouter le texte "Brouillon".
- Créer une deuxième balise option dans la balise select. Donner à cette option la valeur "publie" à l'aide de l'attribut HTML value et lui ajouter le texte "Publié".
- Créer une première balise option dans la balise select. Donner à cette option la valeur "archive" à l'aide de l'attribut HTML value et lui ajouter le texte "Archivé".
- Sous la liste déroulante, ajouter un paragraphe d'aperçu affichant : "Ticket : " suivi de la propriété titre de l'objet ticket.
- Créer un seconde paragraphe affichant "Statut : "suivi de la propriété statut de l'objet ticket.
- Tester : changer la sélection dans la liste doit mettre à jour immédiatement la propriété ticket.statut et l'aperçu.
Exercice 05 : Découvrir les modificateurs de la directive v-model en additionnant des valeurs numériques saisies par l'utilisateur
Objectif : Lier deux champs numériques à des données réactives pour calculer un total d'heures. Constater le problème sans .number (concaténation) puis corriger avec le modificateur.
Instructions :
- Dans la partie <script>, importer ref depuis Vue.
-
Déclarer deux constantes réactives :
- heures initialisée à 0 via ref() (heures standard) ;
- heuresSup initialisée à 0 via ref() (heures supplémentaires).
- Dans la partie <template>, ajouter un sous-titre <h2> avec le texte "Exo 5".
- Dans la section <template>, créer un <fieldset> avec le titre "Temps de travail" placé dans une balise <legend>.
-
Créer une première paire libellé/champ permettant à l'utilisateur de saisir le nombre d'heures planifiées sur la période :
- libeller le champ (ex. : "Heures standard") en le liant avec for/id ;
- utiliser un champ de type text ;
- lier le champ à la donnée avec v-model sur heures (sans modificateur) ;
-
Créer une deuxième paire libellé/champ permettant à l'utilisateur de saisir le nombre d'heures supplémentaires effectuées :
- libeller le champ (ex. : "Heures supplémentaires") ;
- utiliser un champ de type text ;
- lier le champ à la donnée avec v-model sur heuresSup (sans modificateur) ;
- Sous les champs, ajouter un paragraphe d'aperçu affichant : "Total heures : " suivi d'une expression dans les moustaches qui additionne heures et heuresSup.
-
Tester (toujours sans .number) :
entrer 2 en heures standard et 5 en heures supplémentaires.
Constater que le total peut s'afficher comme "25" au lieu de "7".
Pourquoi ? Sans le modificateur, les champs renvoient des chaînes, et l'opérateur + concatène des chaînes. - Corriger les liaisons pour convertir automatiquement les saisies en nombres en appliquant le modificateur number sur la directive v-model des champs Heures standard et Heures supplémentaires.
- Re-tester : avec 2 et 5, le total doit maintenant afficher "7".