4. Validation
In the final-form
library it is possible to add field level and
form level validations. jarb-final-form
automatically injects
the field level validation into your forms. But it also allows you
to define custom field level validation as well.
It supports both synchronous and asynchronous validation.
4.1 Synchronous validation
Regular old synchronous validators can be added via the JarbField
’s
validators
property. It is an array of functions were each function
is a validator.
For example we can define the following custom async validators:
/* validation.ts */
/* Validates based on a single field */
export function isEmail(email: string | undefined): string | undefined {
if (email && email.indexOf('@') === -1) {
return 'Not an email!';
} else {
return undefined;
}
}
/* Validates based on a form */
export function isPasswordTheSame(
_: string | undefined,
userForm: any
): string | undefined {
if (userForm.password === userForm.confirmPassword) {
return undefined;
}
return 'Passwords do not match!';
}
And use them inside of a UserForm
.
import React from 'react';
import { Form } from 'react-final-form';
import { JarbField } from '@42.nl/jarb-final-form';
import { isEmail, isPasswordTheSame } from './validation';
/*
Define the array outside of the component so that the same
array is used for every UserForm.
*/
const emailValidators = [isEmail];
const confirmPasswordValidators = [isPasswordTheSame];
function UserForm() {
return (
<Form
onSubmit={onSubmit}
initialValues={initialValues}
render={({ handleSubmit }) => (
<form onSubmit={handleSubmit}>
<JarbField
name="email"
jarb={{ validator: 'User.email', label: "email" }}
validators={emailValidators}
component="input"
type="text"
/>
<JarbField
name="password"
jarb={{ validator: 'User.password', label: "password" }}
component="input"
type="password"
/>
<JarbField
name="confirmPassword"
jarb={{ validator: 'User.password', label: "confirmPassword" }}
component="input"
type="password"
validators={confirmPasswordValidators}
/>
</form>
)}
/>
);
}
4.2 Asynchronous validation
Asynchronous validators can be added via the JarbField
’s
asyncValidators
property. It is an array of functions were each function
is a validator, which returns a promise.
The asyncValidators
will only run after the synchronous validator
have run and have considered the field valid.
The asyncValidators
are also debounced by default at 200
milliseconds.
Via the asyncValidatorsDebounce
this can be changed per field.
/* validation.ts */
export async function isEmailUnique(
email: string | undefined,
allValues: object,
meta?: FieldState<string>
): Promise<string | undefined> {
/* Only run when the field is active */
if (meta && !meta.active) {
return;
}
/*
If it has no value it is considered valid, the required
validator should handle this case!
*/
if (!email) {
return Promise.resolve(undefined);
}
/* Sends request to back-end */
const isTaken = await User.isEmailTaken(email);
return isTaken
? `The ${email} is already used.`
: Promise.resolve(undefined);
}
And use them inside of a user form.
import React from 'react';
import { Form } from 'react-final-form';
import { JarbField } from '@42.nl/jarb-final-form';
import { isEmailUnique } from './validation';
/*
Define the array outside of the component so that the same
array is used for every UserForm.
*/
const asyncEmailValidators = [isEmailUnique];
function UserForm() {
return (
<Form
onSubmit={onSubmit}
initialValues={initialValues}
render={({ handleSubmit }) => (
<form onSubmit={handleSubmit}>
<JarbField
name="email"
jarb={{ validator: 'User.email', label: "email" }}
asyncEmailValidators={emailValidators}
/* Override the default 200 milliseconds */
asyncValidatorsDebounce={1000}
component="input"
type="text"
/>
</form>
)}
/>
);
}