Simplifying Form Handling in Vue Applications with Form JS - Inspired by Inertia JS
Tags: vue, yup, validation, inertiajs
Published: 2024-07-16 19:35:34

Recently I have been working on [FormJs](https://github.com/JoBinsJP/formjs), which is a form helper and wrapper around Axios, inspired by [inertiaJs](https://inertiajs.com). The purpose behind writing this new library was to streamline the process of how we handle the form on the front-end side. Validating forms is a crucial aspect of the form-handling process, and a lot of us who work in front-end development, particularly with Vue, have relied on vee-validate for this purpose. However, [vee-validate](https://vee-validate.logaretm.com) has undergone significant changes and evolved in a different direction.

### For example:

To validate a text field and display an error message using `vee-validate` version 2, we would typically write the following code:

```xml
<input type="text" name="field" v-validate="'required'">
<span>{{ errors.first('field') }}</span>
```

But in version 3, the whole API has been changed, and the same logic should be written as,

```xml
<ValidationProvider name="field" rules="required" v-slot="{ errors }">
  <input type="text" v-model="value">
  <span>{{ errors[0] }}</span>
</ValidationProvider>
```

As, the composition-API has been backported to vue-2, but the `vee-validation` version 2 has very poor support over it.

> The suggestion might be to use the latest version of vee-validate, but many of us might have been stuck in version 2, as they started using it early in the project.

Please see the below link to the migration guide from version 2 to version 3. [Migrate vee-validate from 2x to 3.x](https://vee-validate.logaretm.com/v3/migration.html#migrating-from-2-x-to-3-0)

---

### Formjs

Let's delve into this further by looking at the installation process.

```bash
yarn add formjs-vue2
```

or

```bash
npm install formjs-vue2
```

After installing it, Let's create a user registration form, containing the user's name, email, and password. For simplicity, I have created a simple layout using TailwindCSS,

```xml
<template>
    <div class="container mx-auto">
        <input type="text" class="block border">
        <input type="text" class="block border">
        <input type="text" class="block border">
        <input type="text" class="block border">
        <button type="submit" class="block border">Submit</button>
    </div>
</template>
```

Now let's import the `useForm` composable from `formjs-vue2` and define our fields in the script section

```javascript
<script setup>
import {useForm} from "formjs-vue2";

const form = useForm({
    name: '',
    email: '',
    password: '',
    password_confirm: ''
})
</script>
```

and let's bind the form for the inputs as

```xml
<template>
    <div class="container mx-auto">
        <input type="text" v-model='form.name' class="block border">
        <input type="text" v-model='form.email' class="block border">
        <input type="text" v-model='form.password' class="block border">
        <input type="text" v-model='form.password_confirm' class="block border">
        <button type="submit" class="block border">Submit</button>
    </div>
</template>
```

---

### Front end Validation

Formjs comes with built-in support for yup, a schema-based validation library. Let's proceed with the installation of yup by running the following command:

```bash
yarn add yup
```

and define a form schema as,

```javascript
<script setup>
import { object, string } from 'yup'
    
const formSchema = object({
    name: string().required(),
    email: string().email(),
    password: string().required(),
    password_confirm: string().required()
})
</script>
```

Here is the basic validation rule that has been defined for form, which we will further assign the schema in,

```javascript
<script setup>
...
const form = useForm({
    name: '',
    email: '',
    password: '',
    password_confirm: ''
}, { schema: formSchema })
</script>
```

---

### Validate Form

Validation of the form can be accomplished using the `validate()` method, which validates all fields and attaches the respective errors to each field. For instance, to access the error for the email field, we would use `form.errors.email`, and so on. Additionally, individual fields can be validated by passing them into the `validate('email')` method. In the example below, we are validating the name input as it changes:

```javascript
<input type="text" v-model='form.name' @input="validate('email')" class="block border">
<span v-if="form.errors.name" class='text-red-500' v-text='form.errors.name'/>
```

The complete example would look like this,

```xml
<template>
    <div class="container mx-auto">
        <form @submit.prevent.stop="submit">
            <input
                type="text"
                class="block border border-black"
                v-model="form.name"
                @input="form.validate('name')">
            <span 
                v-if="form.errors.name" 
                class='text-red-500' 
                v-text='form.errors.name'/>

            <input
                type="text"
                class="block border border-black"
                v-model="form.email"
                @input="form.validate('email')">
            <span 
                v-if="form.errors.email" 
                class='text-red-500' 
                v-text='form.errors.email'/>

            <input
                type="password"
                class="block border border-black"
                v-model="form.password"
                @input="form.validate('password')">
            <span 
                v-if="form.errors.password" 
                class='text-red-500' 
                v-text='form.errors.password'/>

            <input
                type="password"
                class="block border border-black"
                v-model="form.password_confirm"
                @input="form.validate('password_confirm')">
            <span 
                v-if="form.errors.password_confirm" 
                class='text-red-500' 
                v-text='form.errors.password_confirm'/>

            <button type="submit" class="block border">Submit</button>
        </form>
    </div>
</template>
<script setup>
import {useForm} from "formjs-vue2";
import {object, string} from 'yup'

const formSchema = object({
    name: string().required(),
    email: string().required().email(),
    password: string().required(),
    password_confirm: string()
        .required()
        .test('passwords-match', 'Passwords must match', function (value) {
            return this.parent.password === value
        })
})

const form = useForm({
    name: '',
    email: '',
    password: '',
    password_confirm: ''
}, {schema: formSchema})

const submit = async () => {
    await form.validate()

    if (!form.hasErrors) {
        form.post('/api/users', {
            onSuccess: (response) => {
                // handle success
            }
        })
    }
}

</script>
```

---

### Backend validation

Formjs includes built-in support for backend validation, with [Laravel's validation error response](https://laravel.com/docs/10.x/validation#validation-error-response-format) being considered the standard response format. Any validation errors will be automatically accessible through `form.errors[field]`.

---

### Demo

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/epe3gjfh8b1q90m08d20.gif)



Link for demo: [https://stackblitz.com/edit/vitejs-vite-4vj3mb?file=src/App.vue](https://stackblitz.com/edit/vitejs-vite-4vj3mb?file=src/App.vue)

---

### Conclusion

👏👏In conclusion, **Form JS** is a powerful form-handling library that simplifies the process of form validation and submission in Vue applications. Inspired by Inertia JS, it features built-in support for Axios and schema-based validation using Yup. Form JS also supports backend validation errors through Laravel's error response format. Its easy-to-use API allows for effortless integration into any Vue project, making form handling a breeze for front-end developers.

Overall, Form JS is a valuable addition to any Vue developer's toolkit and can help streamline the development process, enabling developers to focus on building better user experiences.

Feel free to share your thoughts and opinions and leave me a comment if you have any problems or questions.