1. Gestion des événements natifs
Vue utilise la directive v-on (raccourci @) pour écouter les événements du DOM. Tous les événements natifs HTML sont supportés.
Syntaxe de base
<!-- v-on: (syntaxe longue) -->
<button v-on:click="incrementer">+</button>
<!-- @ (raccourci recommandé) -->
<button @click="incrementer">+</button>
<!-- Expression inline -->
<button @click="compteur++">{{ compteur }}</button>
<!-- Fonction fléchée avec argument -->
<button @click="(e) => supprimer(item.id, e)">Supprimer</button>
Événements courants
<!-- Souris -->
<div @click="handleClick">Click</div>
<div @dblclick="handleDoubleClick">Double-click</div>
<div @mouseenter="survoler(true)" @mouseleave="survoler(false)">Survol</div>
<div @contextmenu.prevent="menuContextuel">Clic droit</div>
<!-- Clavier -->
<input @keydown="handleKeydown" />
<input @keyup="handleKeyup" />
<input @keypress="handleKeypress" />
<!-- Formulaire -->
<input @input="handleInput" /> <!-- chaque frappe -->
<input @change="handleChange" /> <!-- après perte focus -->
<form @submit="handleSubmit"></form>
<input @focus="activer" @blur="desactiver" />
Accéder à l'événement natif
methods: {
handleClick(event) {
// event est l'objet MouseEvent natif
console.log('Position :', event.clientX, event.clientY);
console.log('Cible :', event.target.tagName);
},
// En expression inline : $event est l'objet événement
// @click="traiter($event, autreArg)"
traiter(e, arg) {
console.log(e.type, arg);
}
}
@input vs @change :
@input se déclenche à chaque frappe (temps réel), @change seulement quand l'élément perd le focus. Pour la recherche temps réel, utilisez @input. Pour valider après saisie, utilisez @change.
2. Modificateurs d'événements
Vue propose des modificateurs qui s'ajoutent après le nom de l'événement avec un point (.). Ils remplacent le code boilerplate classique.
Modificateurs essentiels
<!-- .prevent — équivaut à event.preventDefault() -->
<form @submit.prevent="soumettre">...</form>
<a href="..." @click.prevent="naviguer">Lien</a>
<!-- .stop — équivaut à event.stopPropagation() -->
<div @click="clicParent">
<button @click.stop="clicEnfant">
Clic limité à ce bouton
</button>
</div>
<!-- .once — exécuté une seule fois -->
<button @click.once="chargerDonnees">Charger (1 fois)</button>
<!-- .self — seulement si la cible est l'élément lui-même -->
<div @click.self="fermerOverlay" class="overlay">
<div class="modal">...</div> <!-- Clic ici n'active pas fermerOverlay -->
</div>
<!-- .passive — améliore les performances du scroll -->
<div @scroll.passive="handleScroll">...</div>
<!-- .capture — écoute en phase de capture (pas bubbling) -->
<div @click.capture="handleCapture">...</div>
Chaîner les modificateurs
<!-- On peut combiner plusieurs modificateurs -->
<form @submit.prevent.stop="soumettre">...</form>
<button @click.stop.prevent="action">Action</button>
<!-- Attention : l'ordre compte ! -->
<!-- @click.prevent.self → preventDefault d'abord, puis filtre self -->
<!-- @click.self.prevent → filtre self d'abord, puis preventDefault -->
Avant Vue : Il fallait écrire
methods: { soumettre(e) { e.preventDefault(); ... } }. Avec les modificateurs, la logique métier reste pure, sans code DOM.
3. Modificateurs de touche
Vue fournit des modificateurs pour les événements clavier, permettant de filtrer sur des touches spécifiques sans vérifier event.key manuellement.
Touches nommées
<!-- Touches spéciales -->
<input @keyup.enter="valider" />
<input @keyup.esc="annuler" />
<input @keyup.space="togglePlay" />
<input @keyup.tab="focusSuivant" />
<input @keyup.delete="supprimer" />
<input @keyup.backspace="effacer" />
<!-- Touches fléchées -->
<div @keydown.up="precedent" @keydown.down="suivant">
<div @keydown.left="gauche" @keydown.right="droite">
<!-- Chiffres et lettres -->
<input @keyup.a="handleA" />
<input @keyup.1="handleUn" />
Touches de modification (Ctrl, Alt, Shift, Meta)
<!-- Ctrl + S -->
<input @keydown.ctrl.s.prevent="sauvegarder" />
<!-- Ctrl + Z -->
<div @keydown.ctrl.z="annuler" />
<!-- Alt + flèche gauche -->
<div @keydown.alt.left="retour" />
<!-- Shift + Entrée -->
<textarea @keydown.shift.enter="ajouterLigne" />
<!-- Meta (Cmd sur Mac, Windows sur PC) -->
<div @keydown.meta.k="ouvrirRecherche" />
<!-- .exact — seulement si EXACTEMENT cette combinaison -->
<button @click.ctrl.exact="actionCtrl">Ctrl seulement</button>
<button @click.exact="clicSimple">Clic sans modificateur</button>
// Exemple concret : éditeur avec raccourcis clavier
methods: {
gererRaccourci(e) {
// Géré automatiquement par les modificateurs Vue
// Plus besoin de :
// if (e.ctrlKey && e.key === 's') { ... }
}
}
4. Formulaires contrôlés avec v-model
v-model crée une liaison bidirectionnelle entre un champ de formulaire et une propriété de données. La valeur est toujours synchronisée dans les deux sens.
Types d'input
<!-- Input texte -->
<input v-model="texte" type="text" />
<!-- Textarea -->
<textarea v-model="message"></textarea>
<!-- Checkbox simple -->
<input v-model="accepter" type="checkbox" />
<!-- Checkbox groupe -->
<input v-model="langages" value="js" type="checkbox" /> JavaScript
<input v-model="langages" value="vue" type="checkbox" /> Vue
<input v-model="langages" value="ts" type="checkbox" /> TypeScript
<!-- Radio -->
<input v-model="genre" value="homme" type="radio" /> Homme
<input v-model="genre" value="femme" type="radio" /> Femme
<input v-model="genre" value="autre" type="radio" /> Autre
<!-- Select simple -->
<select v-model="ville">
<option value="">Choisir...</option>
<option value="paris">Paris</option>
<option value="lyon">Lyon</option>
</select>
<!-- Select multiple -->
<select v-model="villes" multiple>
<option v-for="v in optVilles" :key="v" :value="v">{{ v }}</option>
</select>
Modificateurs de v-model
<!-- .lazy — met à jour sur @change (pas @input) -->
<input v-model.lazy="texte" />
<!-- .number — convertit automatiquement en Number -->
<input v-model.number="age" type="number" />
<!-- .trim — supprime les espaces au début et à la fin -->
<input v-model.trim="email" />
<!-- Combinaison -->
<input v-model.trim.lazy="recherche" />
Sans .number : Un
<input type="number"> renvoie toujours une string. Le modificateur .number appelle parseFloat() automatiquement. Utile pour éviter "5" + 3 = "53".
5. Validation côté client
La validation en temps réel améliore l'UX. Avec Vue, elle s'implémente avec des propriétés computed et des erreurs conditionnelles.
Approche par computed
data() {
return {
form: { nom: '', email: '', motdepasse: '' },
touche: {} // Indique si l'utilisateur a "touché" le champ
};
},
computed: {
erreurs() {
const e = {};
// Validation nom
if (!this.form.nom.trim()) {
e.nom = 'Le nom est requis.';
} else if (this.form.nom.length < 2) {
e.nom = 'Le nom doit faire au moins 2 caractères.';
}
// Validation email
const emailRe = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!this.form.email) {
e.email = 'L\'email est requis.';
} else if (!emailRe.test(this.form.email)) {
e.email = 'Format email invalide.';
}
// Validation mot de passe
if (this.form.motdepasse.length < 8) {
e.motdepasse = 'Minimum 8 caractères.';
}
return e;
},
formulaireValide() {
return Object.keys(this.erreurs).length === 0;
}
}
Afficher les erreurs conditionnellement
<div class="champ" :class="{ error: touche.nom && erreurs.nom }">
<label>Nom</label>
<input
v-model="form.nom"
@blur="touche.nom = true"
placeholder="Votre nom"
/>
<!-- Afficher seulement si l'utilisateur a interagi avec le champ -->
<p v-if="touche.nom && erreurs.nom" class="msg-erreur">
{{ erreurs.nom }}
</p>
</div>
<!-- Bouton désactivé si invalide -->
<button
type="submit"
:disabled="!formulaireValide"
class="btn"
>Envoyer</button>
Stratégie "touché" : N'affichez pas d'erreur avant que l'utilisateur ait interagi avec le champ (
@blur ou @input). Sinon le formulaire vide apparaît comme "en erreur" dès l'ouverture.
6. Submit et réinitialisation du formulaire
Gestion du submit
<form @submit.prevent="soumettre">
<input v-model="form.email" type="email" />
<button type="submit" :disabled="!formulaireValide || envoi">
{{ envoi ? 'Envoi…' : 'Envoyer' }}
</button>
</form>
data() {
return {
form: { nom: '', email: '', message: '' },
envoi: false,
succes: false,
erreurServeur: null
};
},
methods: {
async soumettre() {
if (!this.formulaireValide) return;
this.envoi = true;
this.erreurServeur = null;
try {
// Simulation d'un appel API
await new Promise(resolve => setTimeout(resolve, 1500));
this.succes = true;
this.reinitialiser();
} catch (err) {
this.erreurServeur = 'Erreur serveur. Réessayez.';
} finally {
this.envoi = false;
}
},
reinitialiser() {
this.form = { nom: '', email: '', message: '' };
this.touche = {};
// Ne pas réinitialiser succes ici,
// on laisse le message de confirmation visible
}
}
Message de confirmation
<!-- Affichage conditionnel : formulaire OU message de succès -->
<div v-if="succes" class="succes-panel">
<h3>✅ Message envoyé !</h3>
<p>Nous vous répondrons sous 48h.</p>
<button @click="succes = false">Nouveau message</button>
</div>
<form v-else @submit.prevent="soumettre">
...
</form>
UX : Désactivez le bouton pendant l'envoi avec
:disabled="envoi" et changez son texte. L'utilisateur sait que quelque chose se passe et ne peut pas soumettre deux fois.