advanced Step 13 of 15

Forms and v-model

Vue.js Development

Forms and v-model

Forms are a core part of most web applications, and Vue makes form handling intuitive with the v-model directive. The v-model directive creates two-way data bindings on form input elements, automatically syncing the input's value with a reactive data property. It works with text inputs, textareas, checkboxes, radio buttons, and select dropdowns, each with slightly different behavior. Combined with Vue's reactivity and computed properties, you can build sophisticated forms with real-time validation, conditional fields, and complex data transformations.

Basic v-model Usage

The v-model directive listens for the appropriate input event and updates the bound data property. It handles different input types automatically.

<template>
  <form @submit.prevent="handleSubmit">
    <!-- Text input -->
    <label>
      Name:
      <input v-model="form.name" type="text" />
    </label>

    <!-- Email input -->
    <label>
      Email:
      <input v-model="form.email" type="email" />
    </label>

    <!-- Textarea -->
    <label>
      Bio:
      <textarea v-model="form.bio" rows="4"></textarea>
    </label>

    <!-- Number with .number modifier -->
    <label>
      Age:
      <input v-model.number="form.age" type="number" />
    </label>

    <!-- Trimmed input with .trim modifier -->
    <label>
      Username:
      <input v-model.trim="form.username" type="text" />
    </label>

    <!-- Lazy update (on change, not input) -->
    <label>
      Notes:
      <input v-model.lazy="form.notes" type="text" />
    </label>

    <button type="submit">Submit</button>
  </form>
</template>

<script>
export default {
  data() {
    return {
      form: {
        name: "",
        email: "",
        bio: "",
        age: null,
        username: "",
        notes: "",
      },
    };
  },
  methods: {
    handleSubmit() {
      console.log("Form data:", this.form);
    },
  },
};
</script>

Checkboxes, Radios, and Selects

<template>
  <form @submit.prevent="handleSubmit">
    <!-- Single checkbox — boolean -->
    <label>
      <input v-model="form.agreed" type="checkbox" />
      I agree to terms
    </label>

    <!-- Multiple checkboxes — array of values -->
    <fieldset>
      <legend>Interests:</legend>
      <label v-for="option in interestOptions" :key="option">
        <input v-model="form.interests" type="checkbox" :value="option" />
        {{ option }}
      </label>
    </fieldset>
    <p>Selected: {{ form.interests.join(", ") }}</p>

    <!-- Radio buttons -->
    <fieldset>
      <legend>Priority:</legend>
      <label v-for="level in ['low', 'medium', 'high']" :key="level">
        <input v-model="form.priority" type="radio" :value="level" />
        {{ level }}
      </label>
    </fieldset>

    <!-- Select dropdown -->
    <label>
      Country:
      <select v-model="form.country">
        <option disabled value="">Select a country</option>
        <option value="us">United States</option>
        <option value="uk">United Kingdom</option>
        <option value="de">Germany</option>
        <option value="jp">Japan</option>
      </select>
    </label>

    <!-- Multi-select — array -->
    <label>
      Skills:
      <select v-model="form.skills" multiple>
        <option value="html">HTML</option>
        <option value="css">CSS</option>
        <option value="js">JavaScript</option>
        <option value="vue">Vue.js</option>
      </select>
    </label>

    <button type="submit">Submit</button>
  </form>
</template>

<script>
export default {
  data() {
    return {
      interestOptions: ["Sports", "Music", "Technology", "Travel", "Art"],
      form: {
        agreed: false,
        interests: [],
        priority: "medium",
        country: "",
        skills: [],
      },
    };
  },
  methods: {
    handleSubmit() {
      console.log("Form data:", JSON.stringify(this.form, null, 2));
    },
  },
};
</script>

Form Validation

<script>
export default {
  data() {
    return {
      form: { email: "", password: "" },
      errors: {},
    };
  },
  methods: {
    validate() {
      this.errors = {};
      if (!this.form.email) {
        this.errors.email = "Email is required";
      } else if (!/^[^@]+@[^@]+\.[^@]+$/.test(this.form.email)) {
        this.errors.email = "Invalid email format";
      }
      if (!this.form.password) {
        this.errors.password = "Password is required";
      } else if (this.form.password.length < 8) {
        this.errors.password = "Password must be at least 8 characters";
      }
      return Object.keys(this.errors).length === 0;
    },
    handleSubmit() {
      if (this.validate()) {
        console.log("Valid form:", this.form);
      }
    },
  },
};
</script>
Tip: For complex forms, consider using a form validation library like VeeValidate or FormKit. They provide declarative validation rules, error messages, and integration with Vue's reactivity system, saving significant development time.

Key Takeaways

  • v-model creates two-way data bindings on form inputs, textareas, and selects.
  • Modifiers .number, .trim, and .lazy transform input values automatically.
  • Checkboxes bind to booleans (single) or arrays (multiple); radios bind to a single value.
  • Implement validation by checking form data in methods and storing errors in a reactive object.
  • Use form validation libraries for complex forms with many rules and custom error messages.