# 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>