# Form Bindings and Validation — Vue.js

Source: https://www.skillbyai.com/en/vue/s-forms

> 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.

![Three ideas: form bindings and validation, class and style bindings, transitions.](assets/figures/vue/section-5-map.svg) — Figure 5.1 — Forms, styles and transitions.

## A sign-up form with computed validation

Bindings, modifiers and submit handling.

```vue
<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.

**Quiz:** What does the .number modifier on v-model do?

- [ ] Allows only digits to be typed
- [x] 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.
