JAVASCRIPT
Vue 3 Reusable Client-Side Form Validation Composable
Build a custom Vue 3 composable to encapsulate and reuse client-side form input validation logic, enhancing component modularity and reusability.
// composables/useFormValidation.js
import { ref, computed } from 'vue';
export function useFormValidation(initialValues, validationRules) {
const formValues = ref({ ...initialValues });
const errors = ref({});
const validateField = (field, value) => {
const rules = validationRules[field];
if (!rules) return;
for (const rule of rules) {
if (!rule.validator(value, formValues.value)) {
errors.value[field] = rule.message;
return;
}
}
delete errors.value[field]; // Clear error if valid
};
const validateAll = () => {
errors.value = {}; // Clear all errors before re-validating
for (const field in formValues.value) {
validateField(field, formValues.value[field]);
}
return Object.keys(errors.value).length === 0;
};
const hasErrors = computed(() => Object.keys(errors.value).length > 0);
const setFieldValue = (field, value) => {
formValues.value[field] = value;
validateField(field, value); // Validate immediately on change
};
return {
formValues,
errors,
hasErrors,
setFieldValue,
validateAll,
};
}
// LoginForm.vue
<template>
<form @submit.prevent="handleSubmit">
<div>
<label for="email">Email:</label>
<input type="email" id="email" :value="formValues.email" @input="e => setFieldValue('email', e.target.value)" />
<span v-if="errors.email" class="error">{{ errors.email }}</span>
</div>
<div>
<label for="password">Password:</label>
<input type="password" id="password" :value="formValues.password" @input="e => setFieldValue('password', e.target.value)" />
<span v-if="errors.password" class="error">{{ errors.password }}</span>
</div>
<button type="submit" :disabled="hasErrors">Login</button>
</form>
</template>
<script setup>
import { useFormValidation } from './composables/useFormValidation';
const { formValues, errors, hasErrors, setFieldValue, validateAll } = useFormValidation(
{ email: '', password: '' },
{
email: [
{ validator: (value) => value.includes('@'), message: 'Invalid email format' },
{ validator: (value) => value.length > 0, message: 'Email is required' },
],
password: [
{ validator: (value) => value.length >= 6, message: 'Password must be at least 6 characters' },
{ validator: (value) => value.length > 0, message: 'Password is required' },
],
}
);
const handleSubmit = () => {
if (validateAll()) {
alert('Form submitted successfully!
' + JSON.stringify(formValues.value));
} else {
alert('Please correct the errors.');
}
};
</script>
<style scoped>
.error {
color: red;
font-size: 0.8em;
margin-left: 10px;
}
div {
margin-bottom: 10px;
}
</style>
How it works: This snippet showcases a reusable Vue 3 composable, `useFormValidation`, for handling client-side form validation. It manages form values and errors reactively, provides methods to validate individual fields or the entire form, and exposes a `hasErrors` computed property. This pattern promotes modularity and allows for consistent validation logic across different forms without repetition.