JAVASCRIPT
Creating a Reusable Form Validation Composable
Build a custom Vue 3 composable for efficient and reusable form validation logic, simplifying error handling and input feedback across multiple forms in your application.
// composables/useFormValidation.js
import { ref, reactive, computed } from 'vue';
export function useFormValidation(initialFormState, validationRules) {
const form = reactive({ ...initialFormState });
const errors = reactive({});
const isValidating = ref(false);
const validateField = (fieldName) => {
const rules = validationRules[fieldName];
if (!rules) return true;
for (const rule of rules) {
if (!rule.validator(form[fieldName])) {
errors[fieldName] = rule.message;
return false;
}
}
delete errors[fieldName]; // Clear error if valid
return true;
};
const validateForm = async () => {
isValidating.value = true;
let formIsValid = true;
for (const fieldName in validationRules) {
if (!validateField(fieldName)) {
formIsValid = false;
}
}
isValidating.value = false;
return formIsValid;
};
const hasErrors = computed(() => Object.keys(errors).length > 0);
return {
form,
errors,
isValidating,
validateField,
validateForm,
hasErrors,
};
}
// Usage Example (e.g., in a component)
/*
<template>
<form @submit.prevent="handleSubmit">
<div>
<label for="username">Username:</label>
<input type="text" id="username" v-model="form.username" @blur="validateField('username')">
<span v-if="errors.username" style="color: red;">{{ errors.username }}</span>
</div>
<div>
<label for="email">Email:</label>
<input type="email" id="email" v-model="form.email" @blur="validateField('email')">
<span v-if="errors.email" style="color: red;">{{ errors.email }}</span>
</div>
<button type="submit" :disabled="isValidating || hasErrors">Submit</button>
</form>
</template>
<script setup>
import { useFormValidation } from './composables/useFormValidation';
const { form, errors, isValidating, validateField, validateForm, hasErrors } = useFormValidation(
{ username: '', email: '' },
{
username: [
{ validator: (value) => value.length > 3, message: 'Username must be at least 4 characters.' },
],
email: [
{ validator: (value) => /.+@.+\..+/.test(value), message: 'Invalid email address.' },
{ validator: (value) => value.length > 0, message: 'Email is required.' },
],
}
);
const handleSubmit = async () => {
const isValid = await validateForm();
if (isValid) {
alert('Form submitted successfully!');
console.log('Form data:', form);
} else {
alert('Please fix the errors in the form.');
}
};
</script>
*/
How it works: This composable, `useFormValidation`, centralizes form validation logic in Vue 3. It takes initial form data and a set of validation rules, returning reactive `form` data, `errors`, a validation status `isValidating`, and functions to `validateField` or `validateForm` entirely. This pattern promotes reusability, reduces boilerplate in components, and provides a clear, reactive way to manage form input states and display validation messages, making complex forms easier to build and maintain.