Advanced rules
createRule
If you want to create a reusable rule, it's advised to create using createRule
. It will help you define the type, the params, the active state etc...
Exemple: Recreating a simple required
rule
import { createRule } from '@regle/core';
import { ruleHelpers } from '@regle/rules';
export const required = createRule({
type: 'required',
validator: (value: unknown) => {
return ruleHelpers.isFilled(value);
},
message: 'This field is required',
});
Let's break down the options
validator
Type: (value, ...params?) => boolean | {$valid: boolean, [x: string]: any}
required
The validator
function is what should define if the field is valid or not. You can write it exactly like a Inline rule.
message
Type: string | (value, ...metadata) => (string | string[])
required
This will define what error message you assign to your rule. It can be a string or a function receiving the value, params and metadata as parameters
type
Type: string
optional
This property define a type of validator, because multiple rules can share the same target as a result (like required
& requiredIf
)
active
Type: boolean | (value, ...metadata) => boolean
optional
This will define the $active
state of the rule. This will compute wether or not the rule is currently validating or not (More informations on the Parameters and active mode)
Parameters and active mode
With createRule
you can easily define a rule that will depends on external parameters, and having an $active
state
Regle will detect that your validator requires parameters and transform your rule to a function accepting the params you declared as either a raw value, a Ref, or a getter function.
myValidator(5);
const max = ref(5);
myValidator(max)
myValidator(() => max.value)
WARNING
If you pass a raw value as a parameter, it will only be reactive if all your rules are declared as a computed or a getter function
// Getter rule function
useRegle({}, () => ({}))
const rules = computed(() => ({}))
useRegle({}, rules);
Exemple: Recreating requiredIf
rules
import { createRule, useRegle } from '@regle/core';
import { ruleHelpers } from '@regle/rules';
import { ref } from 'vue';
export const requiredIf = createRule({
type: 'required',
validator(value: unknown, condition: boolean) {
// Params like `condition` will always be unwrapped here
// no need to check if it's a value, a ref or a getter function
if (condition) {
return ruleHelpers.isFilled(value);
}
return true;
},
message(value, {$params: [condition]}) {
return `This field is required`,
}
active(value, { $params: [condition] }) {
return condition;
},
});
<script setup lang='ts'>
import { useRegle } from '@regle/core';
const condition = ref(false);
const {regle, errors, resetAll} = useRegle({name: ''}, {
name: {required: requiredIf(condition)}
})
</script>
<template>
<div>
<input v-model="condition" type='checkbox'/>
<label>The field is required</label>
</div>
<div>
<!-- Here we can use $active to know if the rule is enabled -->
<input
v-model='form.name'
:placeholder='`Type your name${regle.$fields.name.$rules.required.$active ? "*": ""}`'/>
<button type="button" @click="resetAll">Reset</button>
</div>
<ul v-if="errors.name.length">
<li v-for="error of errors.name" :key='error'>
{{ error }}
</li>
</ul>
</template>
Result:
Async rules
Async rules let you handle validations that are only possible on server, or expensive local ones. It will update the $pending
status each time it's called.
<template>
<div class="demo-container">
<div>
<input
v-model="form.email"
:class="{ pending: regle.$fields.email.$pending }"
placeholder="Type your email"
/>
<button type="button" @click="resetAll">Reset</button>
<button type="button" @click="validateState">Submit</button>
</div>
<span v-if="regle.$fields.email.$pending"> Checking... </span>
<ul v-if="errors.email.length">
<li v-for="error of errors.email" :key="error">
{{ error }}
</li>
</ul>
</div>
</template>
<script setup lang="ts">
import { createRule, useRegle, type Maybe } from '@regle/core';
import { email, ruleHelpers } from '@regle/rules';
import { ref } from 'vue';
const checkEmailExists = createRule({
async validator(value: Maybe<string>) {
if (ruleHelpers.isEmpty(value) || !email.exec(value)) {
return true;
}
await timeout(1000);
return randomBoolean();
},
message: 'This email already exists',
});
const form = ref({ email: '' });
const { regle, errors, resetAll, validateState } = useRegle(form, {
email: { email, checkEmailExists },
});
</script>
Metadata
Like in inline rules, you can return any data from your validator function as long as it returns at least $valid: boolean
It can be useful for returning computed data from the validator, or in async function to process api result, or api errors.
import { createRule } from '@regle/core';
export const example = createRule({
validator: (value) => {
if (value === 'regle') {
return {
$valid: false,
foo: 'bar'
}
}
return true;
},
message(value, {foo}) {
return 'Error example';
},
});