# Validate mixin
The validate
mixin appends a reactive validation to the model data. It allows to dynamically validate data fields and provides validation error messages.
# Validation pattern
A validation pattern consists of two parts: a constant object with validation rules and a type of generated model validation property.
# Validation rule
Validation rule is a function that receives several parameters, including a value of the model data field, and returns a boolean result or an error message:
/**
* A function type that checks a value from the data path and returns a result.
*
* @param value - checking data field
* @param data - model data
* @param path - field path
* @returns - validation result
*/
type ValidationRule = (value: any, data: unknown, path: string[]) => boolean | string;
For example, let's write an age validation rule:
# Validation rules object
Rules should be placed into an object corresponding to the model data:
Rules of internal objects and arrays may be declared using $sub
, $each
, and $self
reserved words.
$sub
defines a rules set of the internal object fields$each
defines a rules set of each item of the internal array$self
defines a rule of the entire object or array
# Validation property type
A validation property type provides a type helper corresponding to the model data type. This type can be defined manually (e.g. for recursive types) and calculated automatically using PatternAssert
.
The manual definition must be based on ValidationBase
type:
import type { ValidationBase } from '@vueent/mix-models';
// ... definitions of the `Data` type and the `validations` object.
interface CredentialsValidations extends ValidationBase {
readonly c: {
firstName: ValidationBase;
lastName: ValidationBase;
};
}
interface PhoneValidations extends ValidationBase {
readonly c: {
value: ValidationBase;
};
}
interface PhonesValidations extends ValidationBase {
readonly c: PhoneValidations[];
}
interface Validations extends ValidationBase {
readonly c: {
age: ValidationBase;
credentials: CredentialsValidations;
phones: PhonesValidations;
};
}
The calculated definifion uses a model data type and a validation rules object:
import type { PatternAssert } from '@vueent/mix-models';
// ... definitions of the `Data` type and the `validations` object.
type Validations = PatternAssert<typeof validations, Data>;
The two definitions above are equal.
# Model definition
The mixin provides a factory function that returns a mixin function. The following example presents a typical use of the validate
mixin:
The mixin is most useful with TypeScript, because it allows a developer to use the validations autocompletion.
# Usage
A model instance with validate
mixin has two equal properties validations
and v
. The validations
property is of type ValidationBase
, while the v
property is of user-defined type Validations
(see above):
const instance = create();
instance.v.c. // | Autocomplition
// | age
// | credentials
// | phones
instance.v.c.phones.c[0].c. // | Autocomplition
// | value
# Nesting levels with flags, options, and methods
v
/validations
object is divided into levels according to the data model structure and is synchronously updated when it changes. Because any validated field contains many flags, options, and methods, each sublevel is accessible through a c
or an untyped children
property. Every validation object (including the root property v
/validations
) contains the following flags and methods:
c
/children
- children validationsanyChildDirty
- indicating that at least one of children has the dirty flagselfDirty
- indicating that the current data field is dirtydirty
- indicating that the current data field or one of children is dirtyanyChildInvalid
- indicating that at least on of children has the invalid flagselfInvalid
- indicating that validation of the current data field failedinvalid
- indicating the validation of the current data field or one of children failedmessage
- validation error testdirtyMessage
- validation error text, which is specified only if thedirty
flag is settouch()
- marks the current data field is dirtyreset()
- resets the current validation state to default valuescheckValue()
- compares the specified value with the cached value of the current data fielddestroy()
- destroys the current validation instance and its children (should not be called manually)
The following example demonstrates a usage of the model, defined above:
const instance = create();
console.log(instance.v.c.age.dirty); // outputs: false
instance.data.age = 30;
console.log(instance.v.c.age.dirty); // outputs: false
console.log(instance.v.c.age.invalid); // outputs: false
instance.v.c.age.touch();
console.log(instance.v.c.age.dirty); // outputs: true
console.log(instance.v.c.age.invalid); // outputs: false
console.log(instance.v.c.age.message); // outputs: ''
console.log(instance.v.c.age.dirtyMessage); // outputs: ''
instance.data.age = -1;
console.log(instance.v.c.age.dirty); // outputs: true
console.log(instance.v.c.age.invalid); // outputs: true
console.log(instance.v.c.age.message); // outputs: "Age cannot be a negative value."
console.log(instance.v.c.age.dirtyMessage); // outputs: "Age cannot be a negative value."
// do not use push and splice, it breaks reactivity.
instance.data.phones = [...instance.data.phones, { value: '+155533344' }];
// some children are invalid.
console.log(instance.v.invalid); // outputs: true
console.log(instance.v.dirty); // outputs: true
// autotouch is not enabled.
console.log(instance.v.c.phones.c[0].c.value.dirty); // outputs: false
console.log(instance.v.c.phones.c[0].c.value.invalid); // outputs: true
console.log(instance.v.c.phones.c[0].c.value.dirtyMessage); // outputs: ""
instance.v.c.phones.c[0].c.value.touch();
console.log(instance.v.c.phones.c[0].c.value.dirty); // outputs: true
console.log(instance.v.c.phones.c[0].c.value.dirtyMessage); // outputs: "Invalid phone number format."
instance.data.phones[0].value = '+15553334411';
console.log(instance.v.c.phones.c[0].c.value.invalid); // outputs: false
instance.v.reset(); // reset validations
console.log(instance.v.dirty); // outputs: false
instance.destroy();
# Combination with the Save and the Rollback mixins
Validation properties will be reset when the model instance is saved or rolled back.
# v9s integration
Our side project v9s (opens new window) can be integrated with the validate
mixin to make model definition more declarative. For example, let's rewrite the model from the page: