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.

Need help integrating this into your project?

Our team of expert developers can help you build your custom application from scratch.

Hire DigitalCodeLabs