Create custom validation rules for form components

Create custom validation rules for form components with sync and async validators

Component Validation

Creating Custom Validation

Custom validation allows you to create your own validation rules for form components. This is useful when standard validation rules don't meet your requirements.

1. Validator Structure

A custom validator could be asynchronous and should return ValidationResultI that corresponds next structure:

interface ValidationResultI {
  isValid: boolean; // Whether validation passed
  errorKey?: string; // Error key that will be used for i18n (if isValid === false)
}

type ValidatorFunction = (value: string) => ValidationResultI | (value: string) => Promise<ValidationResultI>;

2. Custom Validator Example

Let's create a custom email input field. Here are the steps that should be done:

2.1 Creating Validators

Example of an email format validator:

// src/components/MyComponent/validators/emailValidator.js
export const email = () => (value) => {
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  const isValid = emailRegex.test(value);
  return {
    isValid,
    errorKey: isValid ? null : "EMAIL_INVALID",
  };
};

Example of an email existence validator:

// src/components/MyComponent/validators/emailExistsValidator.js
export const emailExists = () => async (value) => {
  try {
    const url = "https://example.org/emails/exists";
    const response = await fetch(`${url}?email=${value}`);
    const isValid = !response.ok;
    return {
      isValid,
      errorKey: isValid ? null : "EMAIL_ALREADY_EXISTS",
    };
  } catch {
    return {
      isValid: false,
      errorKey: "EMAIL_ALREADY_EXISTS",
    };
  }
};

2.2 Creating Control

WcControlledElement provides logic for adding validation to the component. To use it, call createControlWithValidation during connectedCallback phase, it accepts next arguments:

ParameterTypeDefault ValueDescription
validatorsValidatorFunction[][]Array of validators
optionsCreateControlWithValidationOptionsIConfiguration options

CreateControlWithValidationOptionsI has next structure:

Field NameTypeDefault ValueDescription
elementHTMLElementrootEl.querySelector('input')HTML element for validation
tooltipElementHTMLElementrootEl.querySelector('.input-tooltip')Element for displaying tooltips
validCallback() => void() => Callback for successful validation
invalidCallback() => void() => Callback for failed validation
pendingCallback() => void() => Callback during validation
elementToListenHTMLElementValue of element fieldElement for event listening
validationMapRecord<string, ValidatorFunction>Map of validators

Usage example:

// src/components/MyEmailInputComponent/MyEmailInputComponent.js
import manifest from "./MyEmailInputComponentManifest.manifest";
import WcControlledElement from "@claspo/components/WcControlledElement";
import { emailExists } from "./validators/emailExistsValidator";
import { email } from "./validators/emailValidator";

export default class MyEmailInputComponent extends WcControlledElement {
  static define = {
    name: "my-email-input",
    model: manifest.name,
    manifest: manifest,
  };
  manifest = manifest;

  connectedCallback() {
    // Validators provided via this map could be enabled/disabled from props
    const validationMap = {
      EMAIL_EXISTS: emailExists(),
    };
    this.control = this.createControlWithValidation([email()], {
      validationMap,
    });
  }
}

2.3 Adding Error Translations

Add validation error translations to the component's manifest. Translation keys are formed using the pattern: control,validation,validationErrors,${VALIDATOR_ERROR_KEY}

// src/components/MyEmailInputComponent/MyEmailInputComponent.manifest.js
const manifest = {
  //...
  i18n: {
    en: {
      //...
+      "control,validation,validationErrors,EMAIL_EXISTS": "Email already exists",
+      "control,validation,validationErrors,EMAIL_INVALID": "Email format is invalid",
    },
    ua: {
      //...
+      "control,validation,validationErrors,EMAIL_EXISTS":  "Ця ел. пошта вже існує",
+      "control,validation,validationErrors,EMAIL_INVALID": "Невірний формат ел. пошти",
    },
    //...repeat for rest languages
  },
  //...
};

2.4 Configuring Validation via Props

Validation can be configured through ContactMappingOptionI or directly via component props:

// src/components/MyEmailInputComponent/MyEmailInputComponent.manifest.js
const manifest = {
  // ...
  defaultProps: {
    control: {
+      validation: {
+        required: true,
+        validator: "EMAIL_EXISTS",
+      },
    },
  },
};
export default manifest;

Note: Each component has a built-in required validator, so there is no needed to implement it manually.