Building a Basic Form with Vue

Dec 5, 2019

Vue's v-model directive binds the value of an HTML input tag to a JavaScript variable. That makes it ideal for building a form.

For example, suppose you're building a login form that you want to submit with a HTTP POST request using Axios. Here's how that looks:

const app = new Vue({
  // `v-model` binds `email` to a text input in the template,
  // and `password` to a password input.
  data: () => ({ email: '', password: '' }),
  methods: {
    submit: async function() {
      await axios.post('http://httpbin.org/post', {
        email: this.email,
        password: this.password
      });
    }
  },
  template: `
    <div>
      <input
        type="text"
        v-model="email"
        placeholder="Email">
      <input
        type="password"
        v-model="password"
        placeholder="Password">
      <button v-on:click="submit()">
        Submit
      </button>
    </div>
  `
});

Basic Form Validation

Computed properties are a neat way to handle form validation in Vue. You can define a emailError property that Vue then updates whenever the email changes. You can then disable the "Submit" button whenever there's an emailError:

const app = new Vue({
  data: () => ({ email: '', password: '' }),
  computed: {
    emailError: function() {
      if (this.email.length === 0) {
        return 'Email is required';
      }
      if (!this.email.includes('@')) {
        return 'Email must contain "@"';
      }
      return null;
    }
  },
  methods: {
    submit: async function() {
      await axios.post('http://httpbin.org/post', {
        email: this.email,
        password: this.password
      });
    }
  },
  template: `
    <div>
      <input
        type="text"
        v-model="email"
        placeholder="Email">
      <input
        type="password"
        v-model="password"
        placeholder="Password">
      <button v-on:click="submit()" v-bind:disabled="emailError">
        Submit
      </button>
      <div>
        {{emailError}}
      </div>
    </div>
  `
});

Computed properties can rely on other computed properties, so you can define a separate isFormValid computed property that relies on computed properties for other fields:

const app = new Vue({
  data: () => ({ email: '', password: '' }),
  computed: {
    emailError: function() {
      if (this.email.length === 0) {
        return 'Email is required';
      }
      if (!this.email.includes('@')) {
        return 'Email must contain "@"';
      }
      return null;
    },
    passwordError: function() {
      if (this.password.length === 0) {
        return 'Password is required';
      }
      return null;
    },
    isFormValid: function() {
      return this.emailError == null && this.passwordError == null;
    }
  },
  methods: {
    submit: async function() {
      await axios.post('http://httpbin.org/post', {
        email: this.email,
        password: this.password
      });
    }
  },
  template: `
    <div>
      <input
        type="text"
        v-model="email"
        placeholder="Email">
      <input
        type="password"
        v-model="password"
        placeholder="Password">
      <button v-on:click="submit()" v-bind:disabled="!isFormValid">
        Submit
      </button>
    </div>
  `
});

Vue School has some of our favorite Vue video courses. Their Vue.js Master Class walks you through building a real world application, and does a great job of teaching you how to integrate Vue with Firebase. Check it out!


Did you find this tutorial useful? Say thanks by starring our repo on GitHub!

More Vue Tutorials