# Validation
validation
composable inspired by vuelidate (opens new window)
WARNING
Currently there's no exported validators
.
# Parameters
import { useValidation } from "vue-composable"; const form = useValidation(options);
Parameters | Type | Required | Default | Description |
---|---|---|---|---|
options | Object | true | Validation input object |
# State
The useValidation
function exposes the following reactive state:
import { useValidation } from "vue-composable"; const form = useValidation(options);
State | Type | Description |
---|---|---|
form | Reactive<Options & Validation<Object>> | Reactive form validation object |
WARNING
The returned value is an reactive()
object, do not deconstruct it.
# Input
The input object can be an ValidationObject or a nested dictionary of ValidationObject.
# ValidationObject
ValidationObject is composed by $value
(ref<any>|any
) and validators
((o: ref<any>|any, ctx: object)=>Promise<boolean>|boolean
).
// function validator type ValidatorFunction<T, TContext = any> = ( model: T, ctx: TContext ) => boolean | Promise<boolean>; // validator object with message type ValidatorObject<T> = { $validator: ValidatorFunction<T>; $message: RefTyped<string>; }; // typed validationObject type ValidationObject<T> = { $value: T | Ref<T>; $touch(): void; $reset(): void; } & Record<string, ValidatorFunction<T> | ValidatorObject<T>>; const validationUsername = useValidation({ $value: ref(""), // required validator, the first argument, is `$value` unwrapped // second argument is the context, equivalent to `validationUsername` required(v, context) { return !!v; // or return !!context.$value; }, // canBeTaken validator, returns a promise canBeTaken(v) { return api.get("username/check/" + v); // Promise }, containsInvalidWords: { $validator(v) { return api.get("username/invalid/" + v); }, $message: `This username contains improper words`, // custom properties $customProp: "custom", }, // custom properties $placeholder: "Username", // it will be unchanged, because it starts with `$` });
TIP
You can store any value you want, by using $
as the first letter of the property name.
{ $value: ref(''), $myBag: { awesome: 'property'}, required, // validator }
# Return
It will return an reactive object.
interface ValidationValue<T> { $value: T; $dirty: boolean; // dirty is set to true when `$value` changes for the first time $anyInvalid: boolean; // any validation invalid $errors: any[]; // array of errors toObject(): T; $touch(): void; $reset(): void; } // validator interface ValidatorResult { $error: any; $invalid: boolean; } interface ValidatorResultPromise { $pending: boolean; $promise: Promise<boolean> | null; } interface ValidatorResultMessage { $message: string; }
On the example above, the result will be:
validationUsername.$value; // access to value, validationUsername.$dirty; // was it modified // validators // common validationUsername.required.$error; validationUsername.required.$invalid; // true if the return from the validator is false // promise validationUsername.canBeTaken.$pending; // is promise still executing validationUsername.canBeTaken.$promise; // access to the internal promise // validator object // contains the same properties has the previous and adds $message validationUsername.containsInvalidWords.$message; // message validationUsername.containsInvalidWords.$customProp; //custom prop // custom properties validationUsername.$placeholder; // custom prop // retrieve value object validationUsername.toObject(); // returns string
# NestedValidationObject
The validation composable allows you to have as many nested objects as you want, the only requirement is it ends on a ValidationObject;
interface ValidationGroupResult { $anyDirty: boolean; $errors: Array<any>; $anyInvalid: boolean; } const form = useValidation({ settings: { email: { $value: ref(""), }, // ...etc }, personal: { name: { first: { $value: ref(""), // validators... }, last: { $value: ref(""), // validators... }, }, // ...etc }, }); form.$anyDirty; form.$anyInvalid; form.$errors; form.settings.$anyDirty; form.settings.$anyInvalid; form.settings.$errors; form.personal.$anyDirty; form.personal.$anyInvalid; form.personal.$errors; form.toObject(); // returns { settings: { email: '' }, personal: { name: { first: '', last: '' } } } form.$touch(); // sets all the validations to `$dirty: true` form.$reset(); // sets all the validations to `$dirty: false`
interface ValidationGroupResult { $anyDirty: boolean; $errors: Array<any>; $anyInvalid: boolean; }
# Example
Form validation
# Code
<template> <div class="about"> <h1>Form validation</h1> <form @submit="onSubmit"> <input v-model="form.firstName.$value" placeholder="firstName" /> <input v-model="form.lastName.$value" placeholder="lastName" /> <input v-model="form.password.$value" placeholder="password" /> <input v-model="form.samePassword.$value" placeholder="password2" /> <p v-if="form.samePassword.$dirty && form.samePassword.match.$invalid"> {{ form.samePassword.match.$message }} </p> <br /> <input type="submit" v-model="submitText" :class="{ invalid: form.$anyDirty && form.$anyInvalid, dirty: form.$anyDirty && !form.$anyInvalid, error: form.$errors.length > 0, }" /> </form> </div> </template> <script> import { defineComponent, ref, reactive, computed } from "@vue/composition-api"; import { useValidation } from "vue-composable"; const required = (x) => !!x; export default defineComponent({ setup() { const name = ref(""); const surname = ref(""); const password = ref(""); const form = useValidation({ firstName: { $value: name, required, }, lastName: { $value: surname, required, }, password: { $value: password, required: { $validator: required, $message: ref("password is required"), }, }, samePassword: { $value: ref(""), match: { $validator(x) { return x === password.value; }, $message: "Password don't match", }, }, }); const submitText = computed(() => { if (form.$anyDirty && form.$anyInvalid) { return "Invalid form"; } if (!form.$anyDirty) { return "Please populate the form"; } if (form.$errors.length > 0) { console.log(form.$errors); return "Error"; } return "Submit"; }); const onSubmit = (e) => { e.preventDefault(); if (form.$anyInvalid) { alert("invalid form"); } else { const o = form.toObject(); alert(`submit form "${JSON.stringify(o)}"`); console.log("submitted", o); } }; return { onSubmit, submitText, form, }; }, }); </script> <style scoped> .invalid { color: #e0e0e0; background: #282c34; border: none; outline: none; border-radius: 3px; padding: 0.3rem; margin: 0.5rem auto; } .dirty { color: #ffff92; background: #282c34; border: none; padding: 0.3rem; border-radius: 3px; margin: 0.5rem auto; outline: none; } .error { color: #fb686c; background: #282c34; padding: 0.3rem; margin: 0.5rem auto; border: none; outline: none; border-radius: 3px; } </style>