Cours Vue.js · Module 5: Événements et formulaires

Gestion des événements - Boutons de contrôle

6 min de lecture
Dans cette leçon5

Bienvenue dans le Simulateur de Contrôle de NOVA LAB ! Jusqu'ici, les panneaux de la station se contentaient d'afficher des données : ils calculaient, filtraient et rendaient compte. Mais l'équipage doit les piloter, et un bouton qui ne réagit pas au clic n'est qu'une décoration. En JavaScript pur, chaque bouton exigerait de trouver l'élément, de brancher un écouteur avec addEventListener et de penser à le retirer quand le panneau disparaît. Vue le fait pour toi avec une seule directive.

Ce que tu vas apprendre

  • la gestion des événements - comment brancher une procédure sur un bouton, une touche ou un mouvement de souris.
  • les modificateurs d'événements - comment bloquer le rechargement de la page, arrêter la propagation et réagir à des touches précises.
  • v-model - comment relier les champs de formulaire à des variables dans les deux sens.
  • les modificateurs de v-model - comment nettoyer le signal des champs avec .lazy, .number et .trim.
  • les formulaires avancés - la sélection multiple, les options générées depuis un tableau et la validation.
  • le formulaire en pratique et le projet - un enregistrement complet de module et un panneau d'enregistrement de mission.

Gestion basique des événements

La directive v-on écoute un événement du DOM et exécute du code quand il se produit. La forme complète est v-on:click, et le raccourci utilisé au quotidien est @click. La valeur peut être le nom d'une fonction, un method handler, ou un court morceau de JavaScript, un inline handler. Vue les distingue tout seul : un nom comme handleClick ou un chemin comme panel.start est une méthode, et tout le reste, par exemple count++ ou initiateSystem('OXYGEN'), est du code en ligne. D'abord, le template du panneau avec cinq variantes de bouton.

1<template>
2  <div class="control-panel" style="background: #0a0e27; color: #00ff88;">
3    <!-- Syntaxe complète -->
4    <button v-on:click="handleClick" style="background: #00b4d8; color: #0a0e27;">Activer</button>
5
6    <!-- Syntaxe raccourcie (recommandée) -->
7    <button @click="handleClick" style="background: #00b4d8; color: #0a0e27;">Activer</button>
8
9    <!-- Gestionnaire en ligne -->
10    <button @click="powerLevel++" style="background: #00b4d8; color: #0a0e27;">Puissance : {{ powerLevel }}%</button>
11
12    <!-- Avec un argument -->
13    <button @click="initiateSystem('OXYGEN')" style="background: #00b4d8; color: #0a0e27;">Démarrer O2</button>
14
15    <!-- Avec l'objet événement -->
16    <button @click="logEvent($event)" style="background: #00b4d8; color: #0a0e27;">Journaliser la commande</button>
17  </div>
18</template>

Les deux premiers boutons font exactement la même chose et ne diffèrent que par l'écriture. Le troisième incrémente powerLevel directement dans le template. Le quatrième appelle une fonction avec un argument, et le cinquième lui passe la variable spéciale $event, que Vue remplit avec l'objet événement natif. Voici maintenant le script avec les fonctions que ces boutons appellent.

1<script setup>
2import { ref } from 'vue'
3
4const powerLevel = ref(0)
5
6function handleClick() {
7  console.log('Système activé !')
8}
9
10function initiateSystem(systemName) {
11  console.log(`Initialisation du système ${systemName}...`)
12}
13
14function logEvent(event) {
15  console.log('Événement :', event.type, event.target)
16}
17</script>

Regarde handleClick : dans le template, nous avons donné seulement le nom, sans parenthèses, donc Vue appelle la fonction et lui passe l'objet événement comme premier argument. Nous ne l'utilisons pas ici, mais il est disponible. Ce qui n'a pas changé par rapport au JavaScript pur : ce sont toujours des fonctions ordinaires et des événements ordinaires du navigateur. Vue ajoute seulement le branchement de l'écouteur et son retrait automatique quand le composant quitte la page.

L'événement et ton propre argument

Parfois, une procédure a besoin à la fois de l'objet événement et d'une information à elle, par exemple le nom d'un système. Dans un inline handler, tu passes alors $event à côté de l'argument. Une forme équivalente est une fonction fléchée qui reçoit l'événement en paramètre.

1<template>
2  <button @click="logAction($event, 'oxygen')">Log O2</button>
3  <button @click="(event) => logAction(event, 'water')">Log H2O</button>
4</template>
5
6<script setup>
7function logAction(event, system) {
8  console.log(system, event.type)
9}
10</script>

Les deux boutons appellent la même fonction et affichent le nom du système et le type d'événement, c'est-à-dire click. C'est toi qui choisis l'ordre des arguments, du moment qu'il correspond à la définition de la fonction. Retiens que dans un inline handler, seul $event désigne l'événement : un simple event ou e est une variable inconnue pour le template.

Événements courants

Après @, tu peux écrire le nom de n'importe quel événement du DOM. Le template ci-dessous est un catalogue des plus courants : événements de souris, de clavier et de formulaire, ainsi que le défilement et le chargement d'une image. Nous avons laissé de côté les fonctions de traitement, car seuls les noms d'événements comptent ici.

1<template>
2  <!-- Événements de la souris -->
3  <div @click="onClick" @dblclick="onDoubleClick">Cliquer</div>
4  <div @mouseenter="onEnter" @mouseleave="onLeave">Survol</div>
5  <div @mousedown="onDown" @mouseup="onUp">Appui</div>
6
7  <!-- Événements du clavier -->
8  <input @keydown="onKeyDown" @keyup="onKeyUp" @keypress="onKeyPress" />
9
10  <!-- Événements de formulaire -->
11  <form @submit="onSubmit">
12    <input @input="onInput" @change="onChange" @focus="onFocus" @blur="onBlur" />
13  </form>
14
15  <!-- Autres -->
16  <div @scroll="onScroll">Contenu défilable</div>
17  <img @load="onLoad" @error="onError" :src="imageUrl" />
18</template>

mouseenter et mouseleave signalent l'entrée du curseur sur un élément et sa sortie, et contrairement à mouseover, ils ne se redéclenchent pas quand le curseur passe d'un élément intérieur à un autre. input arrive après chaque changement du contenu d'un champ, et change après la validation de ce changement. focus et blur signifient l'entrée dans un champ et la sortie. Attention à keypress : selon MDN, cet événement est obsolète (deprecated), donc utilise keydown dans le nouveau code. Vue n'invente pas d'événements ici : ce sont tous des événements natifs du navigateur.

Accéder à l'objet événement

Un method handler reçoit automatiquement l'objet événement comme premier paramètre. Les propriétés les plus utiles sont : target, l'élément sur lequel l'événement s'est produit, clientX et clientY avec la position du curseur dans la fenêtre, buttons avec le masque des boutons de souris enfoncés, et pour le clavier key, code et les indicateurs ctrlKey et shiftKey.

1<template>
2  <button @click="handleClick">Cliquer</button>
3  <input @keyup="handleKey" />
4</template>
5
6<script setup>
7function handleClick(event) {
8  console.log('Cible :', event.target)
9  console.log('Position :', event.clientX, event.clientY)
10  console.log('Boutons :', event.buttons)
11}
12
13function handleKey(event) {
14  console.log('Touche :', event.key)
15  console.log('Code :', event.code)
16  console.log('Ctrl :', event.ctrlKey)
17  console.log('Shift :', event.shiftKey)
18}
19</script>

key donne la valeur de la touche, par exemple "Enter" ou "a", et code sa place physique sur le clavier, par exemple "KeyA", quelle que soit la disposition. Pour les raccourcis qui réagissent à un caractère, je te recommande key, car les modificateurs de touches de la prochaine leçon reposent dessus. Choisis code pour les commandes qui dépendent de la position des touches, comme WASD. Avec buttons, garde en tête que pendant un événement click le bouton est parfois déjà relâché, et que le bouton qui a déclenché l'événement est indiqué par la propriété button.

Souviens-toi : @ est le câble entre un bouton du panneau et une procédure de l'ordinateur de bord - tu lui donnes le nom d'une fonction ou une courte commande, et Vue branche et débranche le câble tout seul.

Code de cette leçon : App.vue
1<script setup>
2import { ref } from 'vue'
3
4const signalsSent = ref(0)
5const lastSignalType = ref('')
6const emergencyActive = ref(false)
7
8const sendSignal = (type) => {
9  signalsSent.value++
10  lastSignalType.value = type
11}
12
13const activateEmergency = (event) => {
14  event.preventDefault()
15  emergencyActive.value = true
16  setTimeout(() => {
17    emergencyActive.value = false
18  }, 3000)
19}
20
21const sendOnce = () => {
22  console.log('Signal unique envoyé !')
23}
24</script>
25
26<template>
27  <div class="communication-panel">
28    <div class="panel-header">
29      <h2>Panneau de Communication NOVA LAB</h2>
30      <p class="subtitle">Zone de Communication - Gestion des événements</p>
31    </div>
32
33    <div class="stats">
34      <div class="stat-item">
35        <span class="label">Signaux envoyés :</span>
36        <span class="value">{{ signalsSent }}</span>
37      </div>
38      <div class="stat-item">
39        <span class="label">Dernier type :</span>
40        <span class="value">{{ lastSignalType || 'Aucun' }}</span>
41      </div>
42    </div>
43
44    <div class="signal-buttons">
45      <button @click="sendSignal('Routinier')" class="signal-btn routine">
46        Signal de routine
47      </button>
48
49      <button @click="sendSignal('Urgent')" class="signal-btn urgent">
50        Signal urgent
51      </button>
52
53      <button @click.once="sendOnce" class="signal-btn once">
54        Unique (.once)
55      </button>
56    </div>
57
58    <div class="emergency-section">
59      <a href="#" @click.prevent="activateEmergency" class="emergency-link">
60        Activer l'alarme (.prevent)
61      </a>
62
63      <div v-if="emergencyActive" class="emergency-alert">
64        ALARME ACTIVE !
65      </div>
66    </div>
67
68    <div class="event-info">
69      <h3>Modificateurs d'événements :</h3>
70      <ul>
71        <li><code>.prevent</code> - empêche l'action par défaut</li>
72        <li><code>.stop</code> - arrête la propagation de l'événement</li>
73        <li><code>.once</code> - l'événement ne se déclenche qu'une seule fois</li>
74      </ul>
75    </div>
76  </div>
77</template>
78
79<style scoped>
80.communication-panel {
81  background: linear-gradient(135deg, #0a0e27 0%, #1a1e3f 100%);
82  color: #00ff88;
83  padding: 30px;
84  border-radius: 15px;
85  border: 2px solid #00b4d8;
86  font-family: 'Courier New', monospace;
87  max-width: 800px;
88  margin: 0 auto;
89}
90
91.panel-header {
92  text-align: center;
93  margin-bottom: 30px;
94  padding-bottom: 20px;
95  border-bottom: 2px solid #00b4d8;
96}
97
98.panel-header h2 {
99  color: #00ff88;
100  margin: 0 0 10px 0;
101  font-size: 28px;
102  text-shadow: 0 0 10px #00ff88;
103}
104
105.subtitle {
106  color: #00b4d8;
107  margin: 0;
108  font-size: 14px;
109}
110
111.stats {
112  display: grid;
113  grid-template-columns: 1fr 1fr;
114  gap: 15px;
115  margin-bottom: 30px;
116}
117
118.stat-item {
119  background: rgba(0, 180, 216, 0.1);
120  padding: 15px;
121  border-radius: 8px;
122  border: 1px solid #00b4d8;
123  display: flex;
124  justify-content: space-between;
125  align-items: center;
126}
127
128.label {
129  color: #00b4d8;
130  font-size: 14px;
131}
132
133.value {
134  color: #00ff88;
135  font-size: 20px;
136  font-weight: bold;
137}
138
139.signal-buttons {
140  display: grid;
141  gap: 15px;
142  margin-bottom: 30px;
143}
144
145.signal-btn {
146  background: linear-gradient(135deg, #00b4d8 0%, #0077b6 100%);
147  color: white;
148  border: none;
149  padding: 15px 30px;
150  border-radius: 8px;
151  font-size: 16px;
152  font-weight: bold;
153  cursor: pointer;
154  transition: all 0.3s ease;
155  font-family: 'Courier New', monospace;
156}
157
158.signal-btn:hover {
159  transform: translateY(-2px);
160  box-shadow: 0 5px 15px rgba(0, 180, 216, 0.4);
161}
162
163.signal-btn.urgent {
164  background: linear-gradient(135deg, #ff6b35 0%, #d64545 100%);
165}
166
167.signal-btn.once {
168  background: linear-gradient(135deg, #9d4edd 0%, #7209b7 100%);
169}
170
171.emergency-section {
172  text-align: center;
173  margin-bottom: 30px;
174  padding: 20px;
175  background: rgba(255, 107, 53, 0.1);
176  border-radius: 8px;
177  border: 1px solid #ff6b35;
178}
179
180.emergency-link {
181  color: #ff6b35;
182  font-size: 18px;
183  text-decoration: none;
184  font-weight: bold;
185  padding: 10px 20px;
186  display: inline-block;
187  border: 2px solid #ff6b35;
188  border-radius: 8px;
189  transition: all 0.3s ease;
190}
191
192.emergency-link:hover {
193  background: rgba(255, 107, 53, 0.2);
194  transform: scale(1.05);
195}
196
197.emergency-alert {
198  margin-top: 15px;
199  padding: 15px;
200  background: #ff6b35;
201  color: white;
202  border-radius: 8px;
203  font-weight: bold;
204  animation: pulse 1s infinite;
205}
206
207@keyframes pulse {
208  0%, 100% { opacity: 1; }
209  50% { opacity: 0.7; }
210}
211
212.event-info {
213  background: rgba(0, 255, 136, 0.05);
214  padding: 20px;
215  border-radius: 8px;
216  border: 1px solid #00ff88;
217}
218
219.event-info h3 {
220  color: #00ff88;
221  margin-top: 0;
222  font-size: 18px;
223}
224
225.event-info ul {
226  margin: 0;
227  padding-left: 20px;
228}
229
230.event-info li {
231  color: #00b4d8;
232  margin: 8px 0;
233}
234
235.event-info code {
236  background: rgba(0, 255, 136, 0.2);
237  padding: 2px 6px;
238  border-radius: 4px;
239  color: #00ff88;
240  font-weight: bold;
241}
242</style>

Tu as repéré une erreur dans cette leçon ?

Teste tes connaissances

Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.

  1. 1. Comment ajoute-t-on un écouteur d'événement de clic dans Vue.js ?

  2. 2. Quel symbole est un raccourci pour la directive v-on ?

Voici 2 des 4 questions de cette leçon. Réponds aux autres dans le jeu.

Tâches pratiques dans le jeu

  • Éditeur de code

    Ajoute un gestionnaire @click qui incrémente un compteur et affiche la valeur.

  • Clique dans l'ordre

    Ordonne la syntaxe pour ajouter un gestionnaire de clic dans Vue :

  • Éditeur de code

    Ajoute les gestionnaires @mouseenter, @mouseleave et @dblclick à un élément div.

  • Tri horizontal

    Ordonne la syntaxe correcte pour un gestionnaire passant l'événement et un argument :