
Validation with yup
This section shows how to integrate the yup library for extra validation of form data before submission. We will handle a simple form with two fields: name and age.
Important!
Client-side validation is not secure and should only be used to improve user experience.
Form markup
Nothing special, except the novalidate attribute on the form element to disable built-in browser 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>Submit</button>
</form>Including the library
For simplicity we will load it via CDN and use a module import.
<script type="module">
import * as yup from 'https://cdn.jsdelivr.net/npm/yup@1/+esm';
</script>Handler
Add a handler for the fetchit:before event to validate the form data.
Add a handler for
fetchit:before.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 the
FormDataandFetchItinstances.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 to a plain 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>Define the schema for the form and set error messages.
Localization
yup offers several ways to localize error messages. We use the simplest for this example.
See the documentation for more options.
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 or older') .integer() .typeError('Must be a number'), }); }); </script>Call the synchronous validation method
validateSync()inside atry..catchto handle validation failures.Info
The
abortEarlyoption controls whether validation stops at the first failure. We set it tofalseso all fields are validated.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 or older') .integer() .typeError('Must be a number'), }); try { formSchema.validateSync(fields, { abortEarly: false }); } catch (err) { } }); </script>In the
catchblock, handle validation errors: usesetError()on theFetchItinstance to set invalid state for fields, and callpreventDefault()on the event to stop 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 or older') .integer() .typeError('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 show a popup error message. For examples with popular notification libraries see 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 or older') .integer() .typeError('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('Please fix the errors in the form'); } }); </script>
That's it. You now have validation with yup. Remember that client-side validation is not secure, so use FormIt validation when calling the snippet, or perform validation in your own snippet as well.
Example snippet call with FormIt validation:
[[!FetchIt?
&form=`form.tpl`
&hooks=`email,FormItSaveForm`
&validate=`name:required,age:required:isNumber:minValue=^18^`
// Optional parameters
&snippet=`FormIt`
&formName=`Form name`
&emailSubject=`Email subject`
]]{'!FetchIt' | snippet: [
'form' => 'form.tpl',
'hooks' => 'email,FormItSaveForm',
'validate' => 'name:required,age:required:isNumber:minValue=^18^',
// Optional parameters
'snippet' => 'FormIt',
'formName' => 'Form name',
'emailSubject' => 'Email subject',
]}See the FormIt validators documentation for the full list.
