Кастомные правила валидации

В библиотеке Ant Design валидация форм реализована через компонент Form и его дочерние элементы Form.Item. Стандартная валидация поддерживает базовые проверки, такие как обязательность поля (required), тип данных (type), минимальная и максимальная длина (min, max), а также регулярные выражения (pattern). Для более сложных сценариев используется кастомная валидация, которая позволяет задавать свои правила проверки данных с гибкой логикой.

Использование функции validator

Ключевой инструмент для кастомной валидации — это свойство validator в объекте rules компонента Form.Item. Оно принимает функцию, которая получает объект с параметрами:

<Form.Item
  label="Пароль"
  name="password"
  rules={[
    {
      validator: (_, value) => {
        if (!value) {
          return Promise.reject(new Error('Пароль обязателен'));
        }
        if (value.length < 8) {
          return Promise.reject(new Error('Минимум 8 символов'));
        }
        if (!/[A-Z]/.test(value)) {
          return Promise.reject(new Error('Должна быть хотя бы одна заглавная буква'));
        }
        return Promise.resolve();
      },
    },
  ]}
>
  <Input.Password />
</Form.Item>

Разбор ключевых моментов:

  • Функция возвращает Promise.resolve() при успешной валидации и Promise.reject(new Error('сообщение')) при ошибке.
  • Символ _ используется для параметра rule, если он не требуется.
  • Можно комбинировать несколько условий в одной функции, обеспечивая комплексную проверку.

Асинхронная валидация

Кастомные валидаторы могут быть асинхронными. Это полезно для проверки данных на сервере, например, уникальности логина:

<Form.Item
  label="Логин"
  name="username"
  rules={[
    {
      validator: async (_, value) => {
        if (!value) return Promise.reject(new Error('Логин обязателен'));
        const isAvailable = await checkUsernameAvailability(value);
        if (!isAvailable) {
          return Promise.reject(new Error('Логин уже занят'));
        }
        return Promise.resolve();
      },
    },
  ]}
>
  <Input />
</Form.Item>

Асинхронная проверка должна возвращать промис. Ant Design корректно обрабатывает async функции и показывает ошибки после завершения запроса.

Передача дополнительных параметров

Кастомный валидатор может использовать внешние параметры через замыкания или контекст формы:

const minAge = 18;

<Form.Item
  label="Возраст"
  name="age"
  rules={[
    {
      validator: (_, value) => {
        if (value < minAge) {
          return Promise.reject(new Error(`Возраст должен быть не меньше ${minAge}`));
        }
        return Promise.resolve();
      },
    },
  ]}
>
  <InputNumber />
</Form.Item>

Это позволяет строить динамические правила без жесткой привязки к компоненту.

Комбинирование стандартных и кастомных правил

Кастомная валидация может работать вместе с предустановленными правилами:

<Form.Item
  label="Email"
  name="email"
  rules={[
    { type: 'email', message: 'Неверный формат email' },
    {
      validator: (_, value) => {
        if (value && value.endsWith('@example.com')) {
          return Promise.reject(new Error('Email на example.com запрещен'));
        }
        return Promise.resolve();
      },
    },
  ]}
>
  <Input />
</Form.Item>

Ant Design проверяет правила последовательно сверху вниз и останавливается на первой ошибке.

Универсальные валидаторы

Для упрощения повторного использования можно создавать универсальные функции:

const validatePassword = (_, value) => {
  if (!value) return Promise.reject(new Error('Пароль обязателен'));
  if (value.length < 8) return Promise.reject(new Error('Минимум 8 символов'));
  if (!/[A-Z]/.test(value)) return Promise.reject(new Error('Должна быть хотя бы одна заглавная буква'));
  return Promise.resolve();
};

<Form.Item label="Пароль" name="password" rules={[{ validator: validatePassword }]}>
  <Input.Password />
</Form.Item>

Такой подход улучшает читаемость кода и снижает дублирование логики в разных полях формы.

Контекст формы и зависимые поля

Ant Design поддерживает валидацию полей, зависящих от других значений формы. Для этого используется метод getFieldValue через объект формы:

<Form form={form}>
  <Form.Item
    label="Пароль"
    name="password"
    rules={[{ validator: validatePassword }]}
  >
    <Input.Password />
  </Form.Item>

  <Form.Item
    label="Подтверждение пароля"
    name="confirm"
    dependencies={['password']}
    rules={[
      {
        validator: (_, value) => {
          if (value !== form.getFieldValue('password')) {
            return Promise.reject(new Error('Пароли не совпадают'));
          }
          return Promise.resolve();
        },
      },
    ]}
  >
    <Input.Password />
  </Form.Item>
</Form>
  • Свойство dependencies гарантирует, что при изменении поля password автоматически будет пересчитана валидация confirm.
  • getFieldValue позволяет получить текущее значение любого поля формы.

Локализация сообщений об ошибках

Для мультиязычных приложений сообщения в кастомных валидаторах можно хранить в отдельном объекте:

const messages = {
  required: 'Поле обязательно',
  minLength: (len) => `Минимум ${len} символов`,
};

const validateUsername = (_, value) => {
  if (!value) return Promise.reject(new Error(messages.required));
  if (value.length < 5) return Promise.reject(new Error(messages.minLength(5)));
  return Promise.resolve();
};

Такой подход позволяет легко менять язык интерфейса без модификации логики валидации.

Практические рекомендации

  • Стараться использовать validator только для нестандартных проверок; базовые правила проще и быстрее задаются через type, required, pattern.
  • Всегда возвращать промис из кастомного валидатора.
  • Разделять логику валидации и UI-компоненты для улучшения читаемости.
  • Использовать dependencies для взаимозависимых полей.
  • Для асинхронной проверки сервером показывать индикатор загрузки, чтобы пользователь понимал процесс проверки.

Кастомная валидация в Ant Design предоставляет полную гибкость и позволяет строить сложные формы с динамической и контекстной логикой, сохраняя удобство встроенных компонентов.