SkillByAIOpen interactive version →

Lesson 13 / 25

Form Bindings and Validation

v-model on native inputs.

v-model on inputs, checkboxes and selects

On native elements v-model keeps an input and a ref in sync: text inputs and textareas bind a string, a single checkbox binds a boolean, several checkboxes bound to one array collect the checked values, radio buttons bind the selected value, and <select> binds the chosen option. Modifiers adjust behaviour: .lazy syncs on change instead of input, .number casts to a number, .trim trims whitespace. A simple validation approach keeps the form data in a ref and derives an errors object with computed, showing messages once a field is touched and blocking submit while errors exist. For larger forms, libraries such as VeeValidate or Vuelidate offer schema-based rules. Always validate again on the server.

Input, appearance and motion

Vue binds form inputs to state, binds classes and styles to data, and animates elements entering and leaving.

Figure 5.1 — Forms, styles and transitions.

A sign-up form with computed validation

Bindings, modifiers and submit handling.

<script setup>
import { reactive, computed, ref } from 'vue'

const form = reactive({ email: '', age: null, plan: 'free', terms: false })
const submitted = ref(false)

const errors = computed(() => ({
  email: /^[^@\s]+@[^@\s]+$/.test(form.email) ? '' : 'Enter a valid email',
  age: form.age >= 18 ? '' : 'You must be 18 or older',
  terms: form.terms ? '' : 'Please accept the terms',
}))
const isValid = computed(() => Object.values(errors.value).every(e => !e))

function onSubmit() {
  submitted.value = true
  if (!isValid.value) return
  // send form to the API here
}
</script>

<template>
  <form @submit.prevent="onSubmit">
    <input v-model.trim="form.email" type="email" />
    <small v-if="submitted && errors.email">{{ errors.email }}</small>

    <input v-model.number="form.age" type="number" />
    <small v-if="submitted && errors.age">{{ errors.age }}</small>

    <select v-model="form.plan">
      <option value="free">Free</option>
      <option value="pro">Pro</option>
    </select>

    <label><input v-model="form.terms" type="checkbox" /> I accept</label>
    <button type="submit">Sign up</button>
  </form>
</template>

Use @submit.prevent on the form

Handling submit on the <form> (with .prevent to stop the page reload) also covers pressing Enter, which a click handler on the button alone would miss.

Quick check: What does the .number modifier on v-model do?

  • Allows only digits to be typed
  • Casts the input value to a number
  • Formats the number with separators
  • Validates that the value is positive
Answer

Casts the input value to a number — Input values are strings by default.