Adding custom validators

Naturally, it is necessary to add custom validation rules.

IMPORTANT: keep in mind that the validation rules built in the frontend will not be used in backend (if not implemented there as well).

Every custom validation rule config must be defined in validators array in root plugin registration object.

 window.registerKaskoPlugin('kasko-plugin', (kasko) => {
   return {
+    validators: [...],
   };
 });

It can contain multiple validation rules, but none of the same name. Framework will throw error if already existing validation rule with same name is present.

Every validation rule must have name property and validate method.

{
  name: 'rule_name',
  validate: () => null,
}

name: string

Name as unique string that will be used in field definition (i.e only_emoji_chars_allowed).

validate: (context: PluginValidationContext) => (null | Record<string, boolean>)

Validation function that has one argument and returns either null or object with validation rule names that contains errors (e.g. { at_least_n_chars_present_in: true } means current field will have error at_least_n_chars_present_in and null means no error).

PluginValidationContext {
  fieldName: string;
  value: string | undefined;
  args: string[];
  disabled: boolean;
}

Note that PluginValidationContext doesn't return any other form values.

If there's a need to access input state or form state inside validator (e.g. to validate current field based on other fields/input value), then you can expose kasko service to validator function and access state or form values using available methods: getState or getFormState.

 {
   name: 'rule_name',
-  validate: customValidation,
+  validate: customValidation(kasko),
 }
- const customValidation = ({ value }) => {
+ const customValidation = (kasko) => ({ value }) => {
+   const formState = kasko.getFormState();
    ...
 }

Example validators

Simple usage

Simple validator that will show error on current input if emoji.

NOTE: Remember to respect required validator - it's ok to allow empty string in this example, because field with this validation rule might not be required.

In this example "a" and "a😀" will fail, but "" and "😀😁" will pass this validation rule.

const ALL_EMOJI = /^[\uD800-\uDBFF\uDC00-\uDFFF]*$/;

window.registerKaskoPlugin('kasko-plugin', (kasko) => {
  return {
    validators: [
      // Adds validation rule `only_emoji_chars_allowed`
      {
        name: 'only_emoji_chars_allowed',
        validate(context) {
          // Show no error if field is not set
          if (!context.value) {
            return null;
          }

          // Detects if current input value contains only emoji chars
          const allCharsAreEmoji = ALL_EMOJI.test(context.value);

          if (!allCharsAreEmoji) {
            // Sets error to current input field
            return {
              only_emoji_chars_allowed: true,
            };
          }

          // Clears error for current input
          return null;
        },
      },
    ],
  };
});

Complex usage

This is a more complex validator that takes in 2 arguments, one of them is a number and second is another field name. Then we check that field names value and detect if current input contains n number of chars that are in other field.

Valid case for this rule would be:

  • Validation definition: "at_least_n_chars_present_in:3,control_field"

  • Current field value: "ace"

  • Form value: { control_field: 'abcde' }

window.registerKaskoPlugin('kasko-plugin', (kasko) => {
  return {
    validators: [
      /**
       * Adds validation rule `at_least_n_chars_present_in`
       *
       * @example usage in field definition:
       * `at_least_n_chars_present_in:3,control_field`
       *
       * It has 2 arguments `3` and `control_field`
       */
      {
        name: 'at_least_n_chars_present_in',
        validate(context) {
          // Show no error if field is not set
          if (!context.value) {
            return null;
          }

          // Accessing arguments from validation definition
          const [minChars, otherFieldName] = context.args;
          // Getting input and form states
          const inputState = kasko.getState('input');
          const formState = kasko.getFormState();

          // Getting value for other field (note that other field might not be inside
          // current form, so we need to also check input state)
          const otherFieldValue = formState[otherFieldName] || inputState[otherFieldName];

          // Creates a regexp for getting `n` chars from other field value
          const containsChars = new RegExp(`^[${otherFieldValue}]*$`).test(context.value);
          const hasMinChars = context.value.length >= parseInt(minChars, 10);

          // Checks with current input value
          if (!(containsChars && hasMinChars)) {
            // Sets error to current input field
            return {
              at_least_n_chars_present_in: true,
            };
          }

          // Clears error for current input
          return null;
        },
      },
    ],
  };
});

Last updated