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

Validation with Iodine

This section shows how to implement client-side validation using the Iodine library. We will handle a simple form with two fields: name and email.

Important!

Client-side validation is not secure and should only be used to improve user experience.

Including the library

For simplicity we will load it via CDN.

html
<script src="https://cdn.jsdelivr.net/npm/@caneara/iodine@8/dist/iodine.min.umd.js" defer></script>

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> E-mail
    <input type="email" name="email" value="[[+fi.email]]" />
    <span data-error="email">[[+fi.error.email]]</span>
  </label>
  <button>Submit</button>
</form>

Handler

Add a handler for the fetchit:before event where validation will run. Step by step:

  1. Add a handler for fetchit:before.

    js
    document.addEventListener('fetchit:before', (e) => {
    
    });
  2. Get references to the FormData and FetchIt instances.

    js
    document.addEventListener('fetchit:before', (e) => {
      const { formData, fetchit } = e.detail; 
    });
  3. Iodine can validate a set of data if you pass it an object whose keys are field names and values are the corresponding values. Convert the FormData instance to a plain object.

    js
    document.addEventListener('fetchit:before', (e) => {
      const { formData, fetchit } = e.detail;
      const fields = Object.fromEntries(formData.entries()); 
    });
  4. Define validation rules for the fields.

    js
    document.addEventListener('fetchit:before', (e) => {
      const { formData, fetchit } = e.detail;
      const fields = Object.fromEntries(formData.entries());
      const rules = { 
        name: ['required', 'minLength:5'], 
        email: ['required', 'email'], 
      }; 
    });
  5. Run validation and store the result.

    js
    document.addEventListener('fetchit:before', (e) => {
      const { formData, fetchit } = e.detail;
      const fields = Object.fromEntries(formData.entries());
      const rules = {
        name: ['required', 'minLength:5'],
        email: ['required', 'email'],
      };
    
      const validation = Iodine.assert(fields, rules); 
    });
  6. If validation succeeds, exit the handler with return. Otherwise call preventDefault() on the event to stop form submission.

    js
    document.addEventListener('fetchit:before', (e) => {
      const { formData, fetchit } = e.detail;
      const fields = Object.fromEntries(formData.entries());
      const rules = {
        name: ['required', 'minLength:5'],
        email: ['required', 'email'],
      };
    
      const validation = Iodine.assert(fields, rules);
      if (validation.valid) { 
        return; 
      } 
    
      e.preventDefault(); 
    });
  7. Loop over the fields.

    js
    document.addEventListener('fetchit:before', (e) => {
      const { formData, fetchit } = e.detail;
      const fields = Object.fromEntries(formData.entries());
      const rules = {
        name: ['required', 'minLength:5'],
        email: ['required', 'email'],
      };
    
      const validation = Iodine.assert(fields, rules);
      if (validation.valid) {
        return;
      }
    
      e.preventDefault();
    
      for (const [ name, field ] of Object.entries(validation.fields)) { 
      } 
    });
  8. For each field: if it is valid, clear its error with clearError(); otherwise set the error with setError().

    js
    document.addEventListener('fetchit:before', (e) => {
      const { formData, fetchit } = e.detail;
      const fields = Object.fromEntries(formData.entries());
      const rules = {
        name: ['required', 'minLength:5'],
        email: ['required', 'email'],
      };
    
      const validation = Iodine.assert(fields, rules);
      if (validation.valid) {
        return;
      }
    
      e.preventDefault();
    
      for (const [ name, field ] of Object.entries(validation.fields)) {
        if (field.valid) { 
          fetchit.clearError(name); 
          continue; 
        } 
    
        fetchit.setError(name, field.error); 
      }
    });

Done. You now have client-side validation with Iodine. 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=`myForm.tpl`,
  &validate=`name:required:minLength=^5^,email:required:email`
]]
fenom
{'!FetchIt' | snippet: [
  'form' => 'myForm.tpl',
  'validate' => 'name:required:minLength=^5^,email:required:email',
]}

See the FormIt validators documentation for the full list.