Interactive Form Validation in Vue3

Selena J
Fortitude Technologies
5 min readFeb 3, 2024
Photo by Scott Graham on Unsplash

Form validation becomes notably streamlined and user-friendly with Vue 3, primarily due to its reactivity system. Vue 3's reactivity system, facilitated by concepts like v-model and computed properties, enables real-time validation, offering users instant feedback as they interact with the form. These features collectively make implementing and maintaining form validation in Vue 3 an intuitive and efficient process, simplifying what can often be a complex aspect of web development.

Form setup

<form @submit.prevent="submitForm">
<div>
<label for="username">Username:</label>
<span class="error" v-if="errors.username && !isUsernameValid">{{ errors.username }}</span>
<input type="text" id="username" v-model="form.username.value" @blur="validateField('username')" />

<label for="email">Email:</label>
<span class="error" v-if="errors.email && !isEmailValid">{{ errors.email }}</span>
<input type="email" id="email" v-model="form.email.value" @blur="validateField('email')" />
</div>
<!-- Add more form fields as needed with onBlur validation -->

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

Here, we define the structure of our form using standard HTML elements. The v-model directive establishes a two-way binding between the input field and our Vue data, enabling real-time updates. The @blur event listener triggers the validateField method when the user leaves the input field, initiating field-specific validation.

When crafting a form, wrapping its elements within a <form> tag brings several benefits. Primarily, it enhances accessibility and usability. Associating form-related elements under a <form> tag allows users to navigate and interact with the form more efficiently using keyboard shortcuts. Additionally, it provides a semantic structure, making the intent of the contained elements clear to both developers and assistive technologies.

In this code snippet, @submit.prevent is an event modifier in Vue that prevents the default form submission behavior. Without it, clicking the submit button would trigger a full-page reload, which is not desirable in a single-page application. By adding .prevent to the @submit event, Vue intercepts the form submission and allows us to handle it programmatically, facilitating tasks like form validation before submitting data.

Script Logic

<script setup>
import { ref, computed } from 'vue';

const form = {
username: ref(''),
email: ref(''),
// Add more form fields as needed
};

const errors = ref({});

const isUsernameValid = computed(() => form.username.value.trim() !== '');
const isEmailValid = computed(() => form.email.value.includes('@'));
// Add more computed properties for other form fields as needed
// Alter computed properties to validate various aspects of the input

const validateField = (field) => {
errors.value[field] = ''; // Clear previous error for the field
if (field === 'username' && !isUsernameValid.value) {
errors.value.username = 'Username is required.';
}
if (field === 'email' && !isEmailValid.value) {
errors.value.email = 'Invalid email address.';
}
// Add more validation checks for other form fields as needed
};

const submitForm = () => {
errors.value = {}; // Clear previous errors
validateField('username');
validateField('email');
// Add more validation checks for other form fields as needed

if (Object.keys(errors.value).length === 0) { //if no errors are found
// Validation passed, implement handling of form submission
console.log('Form submitted successfully!', form);
} else {
// Validation failed, implement handling of failure as needed
console.log('Form has validation errors. Please correct them.');
}
};
</script>

In this segment, we delve into the logic behind form validation and submission. The validateField function checks individual fields for validity, updating error messages accordingly. The submitForm method ensures the entire form is validated before submission, allowing you to handle the data as needed, such as sending it to a server.

Computed properties in Vue are an essential tool for managing reactive data derived from other reactive data. In our form example, isUsernameValid and isEmailValid are computed properties that dynamically calculate whether the respective fields are valid based on specific conditions. Computed properties are cached and only re-evaluated when their dependencies change, optimizing performance and ensuring real-time updates without unnecessary recalculations.

Complete File

<!-- App.vue -->
<template>
<div class="form-card">
<form @submit.prevent="submitForm">
<div>
<label for="username">Username:</label>
<span class="error" v-if="errors.username && !isUsernameValid">{{ errors.username }}</span>
<input type="text" id="username" v-model="form.username.value" @blur="validateField('username')" />

<label for="email">Email:</label>
<span class="error" v-if="errors.email && !isEmailValid">{{ errors.email }}</span>
<input type="email" id="email" v-model="form.email.value" @blur="validateField('email')" />
</div>
<!-- Add more form fields with onBlur validation -->

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

<script setup>
import { ref, computed } from 'vue';

const form = {
username: ref(''),
email: ref(''),
// Add more form fields as needed
};

const errors = ref({});

const isUsernameValid = computed(() => form.username.value.trim() !== '');
const isEmailValid = computed(() => form.email.value.includes('@'));
// Add more computed properties for other form fields as needed

const validateField = (field) => {
errors.value[field] = ''; // Clear previous error for the field
if (field === 'username' && !isUsernameValid.value) {
errors.value.username = 'Username is required.';
}
if (field === 'email' && !isEmailValid.value) {
errors.value.email = 'Invalid email address.';
}
// Add more validation checks for other form fields as needed
};

const submitForm = () => {
errors.value = {}; // Clear previous errors
validateField('username');
validateField('email');
// Add more validation checks for other form fields as needed

if (Object.keys(errors.value).length === 0) { //if no errors are found
// Validation passed, handle form submission
console.log('Form submitted successfully!', form);
// Add logic to send data to the server if needed
} else {
console.log('Form has validation errors. Please correct them.');
}
};
</script>

<style scoped>
.form-card {
max-width: 400px;
margin: 0 auto;
padding: 20px;
border: 1px solid #ccc;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

label {
display: block;
margin-bottom: 8px;
}

input {
width: 100%;
padding: 8px;
margin-bottom: 16px;

border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
}

.error {
color: red;
font-size: 12px;
margin-top: 4px;
}
</style>

Scoped styles enhance the visual presentation of our form while making sure the styles only apply to this file. The styles create a centered, card-like layout, outline input fields, and provide clear error messages for improved user experience.

Conclusion

By embracing the <form> tag, utilizing @submit.prevent, and harnessing computed properties, you not only improve the accessibility and semantics of your forms but also gain powerful tools for handling user interactions and data validation in Vue 3. This combination sets the foundation for creating robust and user-friendly forms in your Vue.js applications. Feel free to explore and expand upon these concepts to suit the unique requirements of your projects.

--

--

Selena J
Fortitude Technologies

Hey there :) I am a recent graduate from UVA and currently a Software Engineer working in full stack development. Join me as I share my tips and experiences!