Skip to content
navy

useValidation

Form validation composable with error handling and auto-scroll to first invalid field.

Live Demo

Basic Usage

vue
<script setup>
import { ref } from "vue";
import { useValidation, isFilled } from "orio-ui";

const name = ref("");

// Validation happens bottom to top, place your validations accordingly
const { checkValidity, errors, clearError } = useValidation([
  { model: name, id: "name", validator: isFilled, message: "Name is required" },
]);

function handleSubmit() {
  if (checkValidity()) {
    // Form is valid, submit data
  }
}
</script>

<template>
  <orio-input
    id="name"
    v-model="name"
    label="Name"
    :error="errors['name']"
    @input="clearError('name')"
  />

  <orio-button @click="handleSubmit">Submit</orio-button>
</template>

Multiple Fields

vue
<script setup>
import { ref } from "vue";
import { useValidation, isFilled } from "orio-ui";

const name = ref("");
const email = ref("");
const message = ref("");

const { checkValidity, errors, clearError, clearAllErrors } = useValidation([
  { model: name, id: "name", validator: isFilled, message: "Name is required" },
  {
    model: email,
    id: "email",
    validator: isFilled,
    message: "Email is required",
  },
  {
    model: message,
    id: "message",
    validator: isFilled,
    message: "Message is required",
  },
]);

function handleSubmit() {
  if (checkValidity()) {
    // All fields valid
  }
}

function handleReset() {
  name.value = "";
  email.value = "";
  message.value = "";
  clearAllErrors();
}
</script>

<template>
  <orio-input
    id="name"
    v-model="name"
    label="Name"
    :error="errors['name']"
    @input="clearError('name')"
  />

  <orio-input
    id="email"
    v-model="email"
    label="Email"
    :error="errors['email']"
    @input="clearError('email')"
  />

  <orio-textarea
    id="message"
    v-model="message"
    label="Message"
    :error="errors['message']"
    @input="clearError('message')"
  />

  <orio-button @click="handleSubmit">Submit</orio-button>
  <orio-button variant="secondary" @click="handleReset">Reset</orio-button>
</template>

Custom Validators

vue
<script setup>
import { ref, isRef } from "vue";
import { useValidation, isFilled } from "orio-ui";

const email = ref("");
const age = ref("");
const password = ref("");

// Custom validator for email format
function isValidEmail(model) {
  const value = isRef(model) ? model.value : model;
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  return emailRegex.test(value);
}

// Custom validator for minimum value
function isAdult(model) {
  const value = isRef(model) ? model.value : model;
  return parseInt(value) >= 18;
}

// Custom validator for password strength
function isStrongPassword(model) {
  const value = isRef(model) ? model.value : model;
  return value.length >= 8 && /[A-Z]/.test(value) && /[0-9]/.test(value);
}

const { checkValidity, errors, clearError } = useValidation([
  {
    model: email,
    id: "email",
    validator: isFilled,
    message: "Email is required",
  },
  {
    model: email,
    id: "email",
    validator: isValidEmail,
    message: "Invalid email format",
  },
  { model: age, id: "age", validator: isFilled, message: "Age is required" },
  {
    model: age,
    id: "age",
    validator: isAdult,
    message: "You must be 18 or older",
  },
  {
    model: password,
    id: "password",
    validator: isFilled,
    message: "Password is required",
  },
  {
    model: password,
    id: "password",
    validator: isStrongPassword,
    message: "Password must be 8+ chars with uppercase and number",
  },
]);
</script>

<template>
  <orio-input
    id="email"
    v-model="email"
    label="Email"
    type="email"
    :error="errors['email']"
    @input="clearError('email')"
  />

  <orio-input
    id="age"
    v-model="age"
    label="Age"
    type="number"
    :error="errors['age']"
    @input="clearError('age')"
  />

  <orio-input
    id="password"
    v-model="password"
    label="Password"
    type="password"
    :error="errors['password']"
    @input="clearError('password')"
  />
</template>

API

useValidation

typescript
function useValidation(rules: ValidationRule[]): {
  checkValidity: () => boolean;
  errors: Record<string, string | null>;
  clearError: (id: string) => void;
  clearAllErrors: () => void;
};

ValidationRule

typescript
interface ValidationRule {
  model: MaybeRef<any>; // The reactive value to validate
  id: string; // Element ID (used for error mapping and scroll)
  validator: (model: MaybeRef<any>) => boolean; // Validation function
  message?: string; // Error message (default: "This field is required")
}

isFilled

Built-in validator for checking if a string or array has content:

typescript
function isFilled(model: MaybeRef<string | []>): boolean;

Parameters

ParameterTypeDescription
rulesValidationRule[]Array of validation rules

Return Value

PropertyTypeDescription
checkValidity() => booleanValidates all rules, returns true if all pass
errorsRecord<string, string | null>Reactive object with error messages by ID
clearError(id: string) => voidClears error for specific field
clearAllErrors() => voidClears all errors

Features

  • Auto-scrolls to first invalid field on validation failure
  • Reactive error state
  • Supports custom validators
  • Multiple validators per field
  • Built-in isFilled validator
  • Works with any input component