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

```diff
 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.

```js
{
  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).

```ts
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`.

```diff
 {
   name: 'rule_name',
-  validate: customValidation,
+  validate: customValidation(kasko),
 }
```

```diff
- 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.

```js
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' }`

```js
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 value for other field (note that other field might not be inside
          // current form, so we need to also check input state)
          const otherFieldValue = kasko.getFormState(otherFieldName) || kasko.getState(`input.${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;
        },
      },
    ],
  };
});
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.kasko.io/kasko-frontend-documentation/core-concepts/validation/custom-validators.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
