Validation with yup
This section is devoted to an example of integrating the yup library for additional validation of form data before sending it to the server. Let's imagine that we need to process a simple form with two fields, name and age.
DANGER
Client-side validation is insecure and should only be implemented for the convenience of the user.
Form layout
Nothing unusual, except for the novalidate
attribute added to the form element. It is needed to disable the browser's built-in validation.
<form action="[[~[[*id]]]]" method="post" novalidate>
<label> Name
<input type="text" name="name" value="[[+fi.name]]" />
<span data-error="name">[[+fi.error.name]]</span>
</label>
<label> Age
<input type="text" name="age" value="[[+fi.age]]" />
<span data-error="age">[[+fi.error.age]]</span>
</label>
<button>Can I?</button>
</form>
Adding library
To keep the example simple, let's connect it via CDN and use import.
<script type="module">
import * as yup from 'https://cdn.jsdelivr.net/npm/yup@1/+esm';
</script>
Handler
Let's get to the most important thing, adding a handler to the fetchit:before
event which will validate the form data.
Let's add a handler to the
fetchit:before
event.html<script type="module"> import * as yup from 'https://cdn.jsdelivr.net/npm/yup@1/+esm'; document.addEventListener('fetchit:before', (e) => { }); </script>
Get references to instances of classes
FormData
andFetchIt
.html<script type="module"> import * as yup from 'https://cdn.jsdelivr.net/npm/yup@1/+esm'; document.addEventListener('fetchit:before', (e) => { const { formData, fetchit } = e.detail; }); </script>
Convert formData into a simple object.
html<script type="module"> import * as yup from 'https://cdn.jsdelivr.net/npm/yup@1/+esm'; document.addEventListener('fetchit:before', (e) => { const { formData, fetchit } = e.detail; const fields = Object.fromEntries(formData.entries()); }); </script>
Let's create a scheme by which our form will be validated and immediately specify error messages.
Localization
The yup library provides several ways to localise error texts. We have chosen the simplest one for our example.
You can find out about all the features in documentation.
html<script type="module"> import * as yup from 'https://cdn.jsdelivr.net/npm/yup@1/+esm'; document.addEventListener('fetchit:before', (e) => { const { formData, fetchit } = e.detail; const fields = Object.fromEntries(formData.entries()); const formSchema = yup.object({ name: yup .string() .required('Enter your name'), age: yup .number() .required('Enter your age') .min(18, 'You must be 18 years old') .integer() .typeError('The field must be a number'), }); }); </script>
Let's call the synchronous validation method
validateSync()
in thetry..catch
block so that we can catch failed attempts.INFO
The
abortEarly
parameter is responsible for aborting validation at the first failure. And we need to validate all fields, so we will specifyfalse
.html<script type="module"> import * as yup from 'https://cdn.jsdelivr.net/npm/yup@1/+esm'; document.addEventListener('fetchit:before', (e) => { const { formData, fetchit } = e.detail; const fields = Object.fromEntries(formData.entries()); const formSchema = yup.object({ name: yup .string() .required('Enter your name'), age: yup .number() .required('Enter your age') .min(18, 'You must be 18 years old') .integer() .typeError('The field must be a number'), }); try { formSchema.validateSync(fields, { abortEarly: false }); } catch (err) { } }); </script>
Now we are interested in the
catch
block, there we will catch all validation errors and use thesetError()
method of theFetchIt
instance of theFetchIt
class to set the invalid state of the fields. Let's also call thepreventDefault()
event method to abort the form submission.html<script type="module"> import * as yup from 'https://cdn.jsdelivr.net/npm/yup@1/+esm'; document.addEventListener('fetchit:before', (e) => { const { formData, fetchit } = e.detail; const fields = Object.fromEntries(formData.entries()); const formSchema = yup.object({ name: yup .string() .required('Enter your name'), age: yup .number() .required('Enter your age') .min(18, 'You must be 18 years old') .integer() .typeError('The field must be a number'), }); try { formSchema.validateSync(fields, { abortEarly: false }); } catch (err) { e.preventDefault(); for (const { path, message } of err.inner) { fetchit.setError(path, message); } } }); </script>
Optionally you can show a popup error message. Examples of connecting popular libraries can be found here.
html<script type="module"> import * as yup from 'https://cdn.jsdelivr.net/npm/yup@1/+esm'; document.addEventListener('fetchit:before', (e) => { const { formData, fetchit } = e.detail; const fields = Object.fromEntries(formData.entries()); const formSchema = yup.object({ name: yup .string() .required('Enter your name'), age: yup .number() .required('Enter your age') .min(18, 'You must be 18 years old') .integer() .typeError('The field must be a number'), }); try { formSchema.validateSync(fields, { abortEarly: false }); } catch (err) { e.preventDefault(); for (const { path, message } of err.inner) { fetchit.setError(path, message); } FetchIt.Message.error('There are items that require your attention'); } }); </script>
That's it! After these steps we will get validation using yup library, but remember, it is not safe on the client side. That's why you should use FormIt validation tools when calling the snippet, or if you use your own snippet, you should do it in it.
An example of a snippet call with FormIt validation:
[[!FetchIt?
&form=`form.tpl`
&hooks=`email,FormItSaveForm`
&validate=`name:required,age:required:isNumber:minValue=^18^`
// Optional
&snippet=`FormIt`
&formName=`Form name`
&emailSubject=`Subject`
]]
{'!FetchIt' | snippet : [
'form' => 'form.tpl',
'hooks' => 'email,FormItSaveForm',
'validate' => 'name:required,age:required:isNumber:minValue=^18^',
// Optional
'snippet' => 'FormIt',
'formName' => 'Form name',
'emailSubject' => 'Subject',
]}
A list of all FormIt validators can be found on the documentation site of the component.