Skip to content
  1. Extras
  2. FetchIt
  3. Validation
  4. Validation with yup

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.

modx
<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.

html
<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.

  1. 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>
  2. Get references to the FormData and FetchIt instances.

    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>
  3. 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>
  4. 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>
  5. Call the synchronous validation method validateSync() inside a try..catch to handle validation failures.

    Info

    The abortEarly option controls whether validation stops at the first failure. We set it to false so 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>
  6. In the catch block, handle validation errors: use setError() on the FetchIt instance to set invalid state for fields, and call preventDefault() 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>
  7. 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:

modx
[[!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`
]]
fenom
{'!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.