JAVASCRIPT

Basic Form Validation with Vue 3 Composition API

Learn to implement flexible form validation logic in Vue 3 using the Composition API and a custom reusable composable for common validation rules.

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

export function useFormValidation(initialFormState, rules) {
  const form = ref({ ...initialFormState });
  const errors = ref({});

  const validateField = (field, value) => {
    errors.value[field] = [];
    if (rules[field]) {
      rules[field].forEach(rule => {
        if (!rule.validator(value, form.value)) {
          errors.value[field].push(rule.message);
        }
      });
    }
  };

  const validateForm = () => {
    let isValid = true;
    for (const field in form.value) {
      validateField(field, form.value[field]);
      if (errors.value[field] && errors.value[field].length > 0) {
        isValid = false;
      }
    }
    return isValid;
  };

  const isFormValid = computed(() => {
    for (const field in errors.value) {
      if (errors.value[field] && errors.value[field].length > 0) {
        return false;
      }
    }
    return true;
  });

  const resetValidation = () => {
    errors.value = {};
  };

  return {
    form,
    errors,
    validateField,
    validateForm,
    isFormValid,
    resetValidation,
  };
}

// App.vue (Example Usage)
<template>
  <form @submit.prevent="submitForm">
    <div>
      <label for="username">Username:</label>
      <input type="text" id="username" v-model="form.username" @blur="validateField('username', form.username)" />
      <p v-if="errors.username && errors.username.length" class="error">{{ errors.username[0] }}</p>
    </div>
    <div>
      <label for="email">Email:</label>
      <input type="email" id="email" v-model="form.email" @blur="validateField('email', form.email)" />
      <p v-if="errors.email && errors.email.length" class="error">{{ errors.email[0] }}</p>
    </div>
    <button type="submit" :disabled="!isFormValid && Object.keys(errors).length > 0">Submit</button>
  </form>
  <p v-if="submittedMessage">{{ submittedMessage }}</p>
</template>

<script setup>
import { ref, watch } from 'vue';
import { useFormValidation } from './composables/useFormValidation';

const initialForm = {
  username: '',
  email: '',
};

const validationRules = {
  username: [
    { validator: value => value.length >= 3, message: 'Username must be at least 3 characters.' },
    { validator: value => /^[a-zA-Z0-9]+$/.test(value), message: 'Username can only contain letters and numbers.' },
  ],
  email: [
    { validator: value => value.includes('@'), message: 'Invalid email address.' },
    { validator: value => value.length > 0, message: 'Email is required.' },
  ],
};

const { form, errors, validateField, validateForm, isFormValid, resetValidation } = useFormValidation(initialForm, validationRules);

const submittedMessage = ref('');

const submitForm = () => {
  if (validateForm()) {
    console.log('Form submitted successfully:', form.value);
    submittedMessage.value = 'Form submitted successfully!';
    form.value = { ...initialForm };
    resetValidation();
    setTimeout(() => submittedMessage.value = '', 3000);
  } else {
    console.log('Form has validation errors.');
    submittedMessage.value = 'Please correct the errors.';
  }
};

watch(form, () => {
}, { deep: true });
</script>

<style>
.error {
  color: red;
  font-size: 0.9em;
}
div {
  margin-bottom: 10px;
}
label {
  display: block;
  margin-bottom: 5px;
}
input {
  padding: 8px;
  border: 1px solid #ccc;
  border-radius: 4px;
}
button {
  padding: 10px 15px;
  background-color: #007bff;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}
button:disabled {
  background-color: #cccccc;
  cursor: not-allowed;
}
</style>
How it works: This snippet provides a reusable `useFormValidation` composable for handling form input validation in Vue 3. It allows defining a schema of validation rules for each form field. The composable tracks form data, validation errors, and provides methods to validate individual fields, the entire form, and a computed property `isFormValid` to check the overall form status. The example demonstrates integrating this composable into a component, showing real-time error messages and disabling a submit button based on validation status.

Need help integrating this into your project?

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

Hire DigitalCodeLabs