JAVASCRIPT

Custom Reactive Form Validation Composable in Vue 3

Build a flexible and reusable custom composable for reactive form input validation in Vue 3, providing immediate feedback to users and improving data quality.

// composables/useFieldValidation.js
import { ref, computed, watch } from 'vue';

export function useFieldValidation(initialValue, rules = []) {
  const value = ref(initialValue);
  const isTouched = ref(false);

  const errors = computed(() => {
    if (!isTouched.value) return [];
    const validationErrors = [];
    for (const rule of rules) {
      const error = rule(value.value);
      if (error) {
        validationErrors.push(error);
      }
    }
    return validationErrors;
  });

  const isValid = computed(() => errors.value.length === 0);
  const errorMessage = computed(() => errors.value[0] || ''); // Display first error

  const touch = () => {
    isTouched.value = true;
  };

  // Watch for changes in value and re-evaluate validity
  watch(value, () => {
    if (isTouched.value) { // Only re-validate if already touched
      // Trigger computed `errors` recalculation
    }
  }, { deep: true }); // Use deep: true if value can be an object

  return {
    value,
    errors,
    isValid,
    errorMessage,
    touch,
    isTouched
  };
}

// App.vue (or any component)
<template>
  <div>
    <h2>User Registration</h2>
    <div>
      <label for="username">Username:</label>
      <input
        type="text"
        id="username"
        v-model="usernameField.value"
        @blur="usernameField.touch()"
      />
      <p v-if="!usernameField.isValid && usernameField.isTouched" style="color: red;">
        {{ usernameField.errorMessage }}
      </p>
    </div>
    <div>
      <label for="email">Email:</label>
      <input
        type="email"
        id="email"
        v-model="emailField.value"
        @blur="emailField.touch()"
      />
      <p v-if="!emailField.isValid && emailField.isTouched" style="color: red;">
        {{ emailField.errorMessage }}
      </p>
    </div>
    <button :disabled="!isFormValid">Submit</button>
  </div>
</template>

<script setup>
import { computed } from 'vue';
import { useFieldValidation } from './composables/useFieldValidation';

// Define validation rules
const required = (val) => (val && val.trim() !== '' ? null : 'Field is required.');
const minLength = (len) => (val) => (val && val.length >= len ? null : `Min length is ${len}.`);
const isEmail = (val) => (/^\S+@\S+\.\S+$/.test(val) ? null : 'Must be a valid email.');

const usernameField = useFieldValidation('', [required, minLength(3)]);
const emailField = useFieldValidation('', [required, isEmail]);

const isFormValid = computed(() => usernameField.isValid.value && emailField.isValid.value);
</script>
How it works: This snippet demonstrates how to create a reusable `useFieldValidation` composable in Vue 3 for reactive form validation. It manages a field's value, tracks its `isTouched` state, and dynamically computes `errors` based on an array of provided validation `rules`. The component consuming this composable can bind `v-model` to `field.value`, call `field.touch()` on blur to activate validation feedback, and display `field.errorMessage`. This approach allows for highly modular and reactive validation logic across multiple form fields without relying on external libraries for basic use cases.

Need help integrating this into your project?

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

Hire DigitalCodeLabs