Building a Form in Vue using Tailwind CSS and VeeValidate (Part 2)
Tags: vue, tailwindcss, webdev
Published: 2021-05-25 19:22:04

This is part 2 of 2 on how to create a simple login form with validation using [Vue.js](https://vuejs.org/), [Tailwind CSS](https://tailwindcss.com/) and [VeeValidate](https://vee-validate.logaretm.com/v3) for validation. 

You can clone the example app from my repo on [Github](https://github.com/maxwelladapoe/vue-tailwind-app).

Link to Part 1 [here] (https://dev.to/maxwelladapoe/building-a-form-in-vue-using-tailwind-css-and-veevalidate-part-1-4kh6)

#Part 2: Creating our form and adding validation
In this Part we will look at creating our form and hooking up Veevalidate for frontend form validation

##Creating the form
Create a new vue component `Login.vue` in your `src/views` folder. You can use the command below
```bash
touch src/views/Login.vue
``` 

paste the below code into the file:
```javascript
<template>
  <div class="flex justify-center items-center mt-52">
    <div class="w-full max-w-xs">
      <form class="bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4">
        <div class="identity-input mb-4">
          <label
            for="identity"
            class="block text-gray-700 text-sm font-bold mb-2"
          >
            Email</label
          >
          <input
            id="identity"
            class="shadow appearance-none borderrounded w-full py-2 px-3 text-gray-700 mb-3 leading-tight focus:outline-none focus:shadow-outline"
            type="text"
            placeholder="Email"
            aria-describedby="emailHelp"
            v-model="email"
          />
          <span class="text-xs text-red-700" id="emailHelp"></span>
        </div>

        <div class="password-input mb-6">
          <label
            for="identity"
            class="block text-gray-700 text-sm font-bold mb-2"
            >Password</label
          >

          <input
            aria-describedby="passwordHelp"
            v-model="password"

            class="shadow appearance-none borderrounded w-full py-2 px-3 text-gray-700 mb-3 leading-tight focus:outline-none focus:shadow-outline"
            id="password"
            type="password"
            placeholder="*******"
          />

          <span class="text-xs text-red-700" id="passwordHelp"></span>
        </div>

        <div class="flex items-center justify-between">
          <button
            class="bg-blue-600 hover:bg-black text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline"
            type="submit"
          >
            Sign In
          </button>
          <a
            class="inline-block align-baseline font-bold text-sm text-blue-500 hover:text-blue-800"
            href="#"
          >
            Forgot Password?
          </a>
        </div>
      </form>
    </div>
  </div>
</template>

<script>
export default {
  name: "Login",
  data() {
    return {
      email: "",
      password: "",
    };
  },
  methods: {
    login() {
      console.log("logging in");
    },
  },
};
</script>

```
##Setting up the Login Route
in your `routes/index.js` file add the below

```javascript

...
import Login from '../views/Login.vue'

...

const routes = [
...,
  {
    path: '/login',
    name: 'Login',
    component: Login
  },
]
...
```

in your App.vue file add 
```javascript
...
   <router-link
            to="/link"
            class="block ml-4 mt-4
            lg:inline-block lg:mt-0 text-blue-200
            hover:text-white"
            >Login</router-link>

...
```

With this in place navigating to `http://localhost:8080/#/login` should look like this :

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3ckvb2u2z7c2v46iqwiw.png)


##Setting up VeeValidate

To add validation we need to install  VeeValidate

```bash
npm install vee-validate --save

```
Once installation is complete in your `main.js` add 
``` javascript
...
import {extend,ValidationObserver, ValidationProvider} from 'vee-validate';
import * as rules from 'vee-validate/dist/rules';


Vue.component('ValidationObserver', ValidationObserver);
Vue.component('ValidationProvider', ValidationProvider);

Object.keys(rules).forEach(rule => {
  extend(rule, rules[rule]);
});
...
```
`extend` Allows you to add more rules to be used globally by the vee-validate functions and components.

`ValidationObserver` is a component that wraps your forms and provides aggregated validation state for all the fields nested under it.

`ValidationProvider` component is a component that wraps your inputs and provides validation states.

We then import all the the default rules using `import * as rules from 'vee-validate/dist/rules';` and iterate over them using the `Object.keys(rules).forEach` function.

With that in place we can move on to making the magic Happen.

##Adding Validation to the form.

We now need to wrap our form component with the ValidationObserver component
```javascript
...
 <ValidationObserver ref="loginForm">
<form >
...
</form>
</ValidationObserver>
...
```

The Validation Provider now goes around our inputs. It accepts a  `v-slot="v"` attribute. To make things a little less verbose we can use ES6's object destructing and replace it with ` v-slot="{ errors }"`. 
It also accepts a name attribute which is used in displaying the error messages and also accepts the validation rules.
[Read more about registering the Validation Provider heere](https://vee-validate.logaretm.com/v3/guide/basics.html#registering-the-validation-provider)
With this in place we can now make use of the errors and display them in our html as well

```javascript
      <ValidationProvider name="Email" rules="required|email" v-slot="{ errors }">
            <div class="email-input mb-4">
              <label
                for="email"
                class="block text-gray-700 text-sm font-bold mb-2"
              >
                Email</label
              >
              <input
                id="email"
                class="shadow appearance-none borderrounded w-full py-2 px-3 text-gray-700 mb-3 leading-tight focus:outline-none focus:shadow-outline"
                type="text"
                placeholder="Email"
                aria-describedby="emailHelp"
                v-model="email"
              />

              <span class="text-xs text-red-700" id="emailHelp">{{
                errors[0]
              }}</span>
            </div>
          </ValidationProvider>

          <ValidationProvider
            name="Password"
            rules="required|min:6"
            v-slot="{ errors }"
          >
            <div class="password-input mb-6">
              <label
                for="password"
                class="block text-gray-700 text-sm font-bold mb-2"
                >Password</label
              >

              <input
                aria-describedby="passwordHelp"
                v-model="password"
                class="shadow appearance-none borderrounded w-full py-2 px-3 text-gray-700 mb-3 leading-tight focus:outline-none focus:shadow-outline"
                id="password"
                type="password"
                placeholder="*******"
              />

              <span class="text-xs text-red-700" id="emailHelp">{{
                errors[0]
              }}</span>
              <span class="text-xs text-red-700" id="passwordHelp"></span>
            </div>
          </ValidationProvider>
```

Finally we need to ensure that the Validation Observer is triggered when the submit button is pressed to ensure that validation has been passed successfully.
We need to add a `v-slot="{ handleSubmit }"` to our Validation Observer and also add an `@submit.prevent` event listener to our form 

```javascript
...
 <ValidationObserver v-slot="{ handleSubmit }" ref="loginForm">
                    <form @submit.prevent="handleSubmit(login)" class="bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4">
...
```
That sums it up. Your page should look like this once you click the submit button without entering any values or when you enter an invalid value for your email / password

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8peoo4dmj0mzle5dqecn.png)