Het patroon — drie stappen
Een formulier in Vue bouwen draait om drie dingen:
- Refs voor elke input-waarde
v-modelom elke input aan zijn ref te koppelen@submit.preventop het<form>om te reageren op verzenden
Belangrijk: deze pagina gaat over het opbouwen van een formulier. De details van v-model op verschillende input-types (radio, checkbox, select) staan op de v-model pagina.
Simpel formulier
<script setup>
import { ref } from 'vue'
const name = ref('')
const email = ref('')
const handleSubmit = () => {
console.log('Verstuurd:', name.value, email.value)
}
</script>
<template>
<form @submit.prevent="handleSubmit">
<label>
Naam:
<input v-model="name" type="text" />
</label>
<label>
Email:
<input v-model="email" type="email" />
</label>
<button type="submit">Versturen</button>
</form>
</template>
Drie dingen om op te merken:
@submit.prevent— voorkomt dat de pagina ververst- De knop heeft
type="submit"— anders triggert hijsubmitniet v-modelregelt automatisch de two-way binding — geen@inputnodig
Simpele validatie
Voor basis validatie heb je twee opties:
Optie 1: check in de submit-handler
<script setup>
import { ref } from 'vue'
const email = ref('')
const error = ref('')
const handleSubmit = () => {
// Reset error
error.value = ''
// Check
if (!email.value.includes('@')) {
error.value = 'Vul een geldig e-mailadres in'
return
}
// Alles goed — versturen
console.log('Versturen:', email.value)
}
</script>
<template>
<form @submit.prevent="handleSubmit">
<input v-model="email" />
<p v-if="error" style="color: red">{{ error }}</p>
<button type="submit">Versturen</button>
</form>
</template>
Optie 2: button disablen met computed
<script setup>
import { ref, computed } from 'vue'
const name = ref('')
const email = ref('')
const isValid = computed(() => {
return name.value.length > 0 && email.value.includes('@')
})
</script>
<template>
<form @submit.prevent="handleSubmit">
<input v-model="name" placeholder="Naam" />
<input v-model="email" placeholder="Email" />
<button type="submit" :disabled="!isValid">
Versturen
</button>
</form>
</template>
Welke optie? Voor beginners is optie 1 (check in de handler) het duidelijkst. Optie 2 (computed + disabled) is mooier maar abstracter. Beide werken prima.
Form resetten na verzenden
Wil je dat het formulier leeg is na verzenden? Zet de refs terug naar hun lege waarde.
<script setup>
import { ref } from 'vue'
const name = ref('')
const email = ref('')
const handleSubmit = () => {
console.log('Versturen:', name.value, email.value)
// Reset
name.value = ''
email.value = ''
}
</script>
Tip: Bij grotere formulieren wordt dat snel veel regels. Dan is het handiger om alle velden in één object te zetten — zie het volgende voorbeeld.
Compleet voorbeeld — registratieformulier
Alles bij elkaar: meerdere velden in één object, validatie, en reset na succes.
<script setup>
import { ref } from 'vue'
const form = ref({
name: '',
email: '',
password: '',
acceptTerms: false
})
const error = ref('')
const success = ref(false)
const handleSubmit = () => {
error.value = ''
success.value = false
// Validatie
if (form.value.name.length < 2) {
error.value = 'Naam is te kort'
return
}
if (!form.value.email.includes('@')) {
error.value = 'Ongeldig email'
return
}
if (form.value.password.length < 8) {
error.value = 'Wachtwoord moet minimaal 8 tekens zijn'
return
}
if (!form.value.acceptTerms) {
error.value = 'Je moet akkoord gaan met de voorwaarden'
return
}
// Verstuur (hier zou normaal een fetch komen)
console.log('Registreren:', form.value)
success.value = true
// Reset
form.value = {
name: '',
email: '',
password: '',
acceptTerms: false
}
}
</script>
<template>
<form @submit.prevent="handleSubmit">
<label>
Naam:
<input v-model="form.name" type="text" />
</label>
<label>
Email:
<input v-model="form.email" type="email" />
</label>
<label>
Wachtwoord:
<input v-model="form.password" type="password" />
</label>
<label>
<input v-model="form.acceptTerms" type="checkbox" />
Ik ga akkoord met de voorwaarden
</label>
<p v-if="error" style="color: red">{{ error }}</p>
<p v-if="success" style="color: green">Account aangemaakt! ✅</p>
<button type="submit">Registreren</button>
</form>
</template>
Het patroon: één form ref met alle velden, één handleSubmit die valideert + verstuurt + reset, en aparte refs voor error en success meldingen.
Belangrijke Regels
- Altijd
@submit.preventop het<form>— anders refresht de pagina - Knop heeft
type="submit"om de submit te triggeren - Gebruik
v-modelop elke input — niet@inputhandmatig - Meer dan 2-3 velden? Zet ze in één object i.p.v. losse refs
- Validatie? Doe het in de submit-handler, return vroeg bij fouten
Veelgemaakte Fouten
Fout — geen .prevent:
<form @submit="handleSubmit"> <!-- ❌ pagina ververst -->
Goed:
<form @submit.prevent="handleSubmit">
Fout — knop zonder type="submit":
<form @submit.prevent="handleSubmit">
<input v-model="name" />
<button>Versturen</button> <!-- ❌ default is geen submit -->
</form>
Goed:
<button type="submit">Versturen</button>
Of zet de actie op de knop: <button @click="handleSubmit"> (maar dan kan de gebruiker niet met Enter versturen).
Fout — v-model én aparte @input:
<input v-model="name" @input="name = $event.target.value" />
<!-- ❌ dubbel werk — v-model doet dit al -->
Goed:
<input v-model="name" /> <!-- ✅ -->