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.

Need help integrating this into your project?

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

Hire DigitalCodeLabs