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>
  `
});

More Vue Tutorials