Advanced topics
Validation

Validation

Validation can be defined for each fields using the required and validation keys.

The required key only specify if the field is required and not null in the values inserted by the form user, while the validation key defines some rules about the inserted value:

Validation modes

LetsForms supports 4 modes of validation:

  • onSubmit (default): validation is executed only on submit
  • onChange: validation is triggered at every value change of any field (can degrade performance)
  • onBlur: validation is triggered every time a field looses focus (can degrade performance)
  • all: all the above (can degrade performance)

Every time validation errors are found, the onError callback is called. The payload is an object: keys are the field names and values are objects with the field labels and the error messages:

{
  "my_field": {
    "label": "My field",
    "name": "my_field",
    "errorMessage": "This is required"
  },
  "my_array": {
    "label": "My field",
    "name": "my_field"
    // "errorMessage": "Minimum length is 2"
    "errorMessages": [
      {
        // nested errors in the nested form inside of array
        "my_field_in_array": {
          "label": "My field in the array",
          "name": "my_field_in_array",
          "errorMessage": "This is required"
        }
      },
      // ...
    ]
  }
}

LetsForm will validate also nested forms of arrays, in that case the key errorMessages is an array of validation object for each of the array items (note that an array component can still have a key errorMessage in case the validation fails at the array component, for example if minLength is not satisifed).

The validation object

KeyTypeDescription
minLengthnumberMinimum length, for string fields and arrays (i.e. input-text, array)
maxLengthnumberMaximum length, for string fields and arrays (i.e. input-text, array)
minnumberMinimum value, for numeric fields (i.e. input-number)
maxnumberMinimum value, for numeric fields (i.e. input-number)
messagestring / i18nThe message to display if validation fails
patternstringA regular expression used to validate a string value, omit the /s (i.e., use [a-z0-9] and not /[a-z0-9]/
validatestringJavaScript code for validation (asynch function)

For example

{
  "$schema": "https://unpkg.com/lets-form/schemas/react-rsuite5/form.json",
  "version": 2,
  "fields": [
    {
      "name": "my_field",
      "label": "My field",
      "component": "input-text",
      "required" true,
      "validation": {
        "minLength": 5,
        "maxLength": 10,
        "message": "There's something wrong with this value"
      }
    }
  ]
}

I18N support

I18N is supported in the validation message key

{
  "$schema": "https://unpkg.com/lets-form/schemas/react-rsuite5/form.json",
  "version": 2,
  "fields": [
    {
      "name": "my_field",
      "label": "My field",
      "component": "input-text",
      "required" true,
      "validation": {
        "minLength": 5,
        "maxLength": 10,
        "message": {
          "en-US": "There's something wrong with this value",
          "it-IT": "C'e' qualcosa che non va in questo valore"
        }
      }
    }
  ]
}

Advanced validation

More complex validation can be implemented with some simple JavaScript using the validate keys

{
  "$schema": "https://unpkg.com/lets-form/schemas/react-rsuite5/form.json",
  "version": 2,
  "fields": [
    {
      "name": "my_field",
      "label": "My field",
      "component": "input-text",
      "required" true,
      "validation": {
        "message": "There's something wrong with this value",
        "validate": "if (value.length < 10) { return false; }"
      }
    }
  ]
}

In this simple example it just checks for the string to be at least 10 chars.

In the JavaScript code, these variables are available:

  • value is the value inserted by the user for the specific field
  • formValues is a hash with all current values of the form

The JavaScript code should return

  • true or null or undefined or nothing if the value is to be considered invalid
  • false if the value is invalid, in that case will be shown the error message defined in message or the default one
  • string or a i18n object if the value is not valid and a customi error should be shown

For example, in order to return a custom localized error message

{
  "$schema": "https://unpkg.com/lets-form/schemas/react-rsuite5/form.json",
  "version": 2,
  "fields": [
    {
      "name": "my_field",
      "label": "My field",
      "component": "input-text",
      "required" true,
      "validation": {
        "message": "There's something wrong with this value",
        "validate": `if (value.length < 10) {
          return {
            'en-US': 'There\'s something wrong with this value',
            'it-IT': 'C\'e\' qualcosa che non va in questo valore'
          };
        }`
      }
    }
  ]
}

Advanced validation with external values

Sometimes it's needed to validate forms based on dynamic values (not included in the JSON form schema). In this scenario the Form Context can be useful.

For example consider the scenario where the username needs to be validated against an array of values of existing usernames:

const SIGNUP_FORM = {
  "$schema": "https://unpkg.com/lets-form/schemas/react-rsuite5/form.json",
  "version": 2,
  "fields": [
    {
      "name": "username",
      "label": "Username",
      "component": "input-text",
      "required" true,
      "validation": {
        "message": "There's something wrong with this value",
        "validate": `if (!value) {
          return 'Missing username';
        }
        if (context('existingUsernames').includes(value)) {
          return 'Username already taken';
        }
        `
      }
    }
  ]
}

In this case we're using the form context to store in existingUsernames the existing usernames, then context() in Form Script to fetch the value in the JavaScript validator

// example of external variables for validation
const usernames = ['my_username', 'another_username', 'some_username'];
 
const MyForm = () => {
  return (
    <LestForm
      form={SIGNUP_FORM}
      context={{
        existingUsernames: usernames
      }}
      // ...
    />
  );
}
⚠️

Pay attention to the context prop in <LetsForm/>, if the value changes it causes the form re-render, the example above is wrong since at every re-render of MyForm a new object is passed to the context prop which causes also a re-render of the form. See Form Context for more info.

Advanced validation with asynchronous call

The validate function is an asinchrous function and it accepts await ... statements. The example above can be rewritten using an external call

const SIGNUP_FORM = {
  "$schema": "https://unpkg.com/lets-form/schemas/react-rsuite5/form.json",
  "version": 2,
  "fields": [
    {
      "name": "username",
      "label": "Username",
      "component": "input-text",
      "required" true,
      "validation": {
        "validate": `if (!value) {
          return 'Missing username';
        }
        const result = await fetch('/my_endpoint?username=' + value);
        if (result.status !== 200) {
          return 'Username already taken';
        }
        `
      }
    }
  ]
}

The validate function can also include imports() statements to include external EE6 modules

const SIGNUP_FORM = {
  "$schema": "https://unpkg.com/lets-form/schemas/react-rsuite5/form.json",
  "version": 2,
  "fields": [
    {
      "name": "vat",
      "label": "VAT",
      "component": "input-text",
      "required" true,
      "validation": {
        "validate": `if (!value) {
          return 'Missing VAT';
        }
        const { checkVAT, italy } = await import('https://cdn.jsdelivr.net/npm/jsvat@2.5.3/+esm');
        const check = checkVAT(value, [italy]);
        if (!check || !check.isValid) {
          return 'Invalid VAT';
        }
        `
      }
    }
  ]
}