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

В библиотеке Naive UI для работы с формами используется компонент n-form, который позволяет управлять состоянием полей и проверять их корректность с помощью системы валидаторов. Помимо встроенных правил (required, type, min, max и др.), часто возникает необходимость создавать кастомные правила валидации, отвечающие специфическим требованиям приложения.

Кастомные правила создаются через объект rules для каждого поля формы. В Naive UI каждое правило описывается как объект со следующими ключевыми свойствами:

  • required — boolean, определяет обязательность поля.
  • trigger — событие, при котором происходит проверка (blur, input или change).
  • validator — функция, реализующая кастомную проверку.

Пример структуры правила с кастомным валидатором:

const rules = {
  username: [
    {
      required: true,
      trigger: 'blur',
      validator: (rule, value) => {
        if (!/^[a-zA-Z0-9_]{4,16}$/.test(value)) {
          return new Error('Имя пользователя должно содержать 4-16 символов и состоять из латиницы, цифр или подчеркивания');
        }
        return true;
      }
    }
  ]
}

Синхронные и асинхронные валидаторы

Функция validator может быть синхронной или асинхронной. Синхронный валидатор возвращает true при успешной проверке или Error при ошибке. Асинхронный валидатор возвращает Promise, что позволяет выполнять проверки, требующие обращения к серверу.

Пример асинхронного валидатора для проверки уникальности email:

const rules = {
  email: [
    {
      required: true,
      trigger: 'blur',
      validator: async (rule, value) => {
        const response = await fetch(`/api/check-email?email=${value}`);
        const data = await response.json();
        if (!data.available) {
          throw new Error('Этот email уже зарегистрирован');
        }
        return true;
      }
    }
  ]
}

Использование нескольких валидаторов на одном поле

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

const rules = {
  password: [
    {
      required: true,
      trigger: 'blur',
      validator: (rule, value) => {
        if (value.length < 8) {
          return new Error('Пароль должен быть не менее 8 символов');
        }
        return true;
      }
    },
    {
      trigger: 'blur',
      validator: (rule, value) => {
        if (!/\d/.test(value)) {
          return new Error('Пароль должен содержать хотя бы одну цифру');
        }
        return true;
      }
    }
  ]
}

Параметры функции валидатора

Функция валидатора получает следующие аргументы:

  • rule — объект текущего правила, включая все его свойства (required, trigger, message и т.д.).
  • value — текущее значение поля, которое проверяется.
  • callback — устаревший способ возвращения ошибки, сейчас предпочтительно использовать return или throw Error.

Триггеры проверки

trigger задает событие, на котором будет выполняться проверка. В Naive UI доступны следующие варианты:

  • blur — проверка при потере фокуса поля.
  • input — проверка при каждом вводе символа.
  • change — проверка при изменении значения (например, для sel ect или checkbox).

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

Общие рекомендации при создании кастомных правил

  1. Ясные сообщения об ошибках. Сообщение должно точно указывать, что необходимо исправить.
  2. Разделение логики. Каждый валидатор отвечает за конкретное правило, что облегчает поддержку и тестирование.
  3. Асинхронные проверки только при необходимости. Запросы к серверу могут замедлить реакцию формы, поэтому их следует использовать только для проверки уникальности или данных, недоступных на клиенте.
  4. Комбинирование с встроенными правилами. Кастомные валидаторы можно сочетать с required, min, max и другими стандартными проверками.

Пример комплексной формы с кастомными правилами

<template>
  <n-form :model="form" :rules="rules" ref="formRef">
    <n-form-item label="Имя пользователя" path="username">
      <n-input v-model:value="form.username" />
    </n-form-item>
    <n-form-item label="Email" path="email">
      <n-input v-model:value="form.email" />
    </n-form-item>
    <n-form-item label="Пароль" path="password">
      <n-input type="password" v-model:value="form.password" />
    </n-form-item>
    <n-button @click="submitForm">Отправить</n-button>
  </n-form>
</template>

<script setup>
import { ref } fr om 'vue'
import { NForm, NFormItem, NInput, NButton } from 'naive-ui'

const formRef = ref(null)
const form = ref({
  username: '',
  email: '',
  password: ''
})

const rules = {
  username: [
    {
      required: true,
      trigger: 'blur',
      validator: (rule, value) => /^[a-zA-Z0-9_]{4,16}$/.test(value) || new Error('Имя пользователя должно быть 4-16 символов')
    }
  ],
  email: [
    {
      required: true,
      trigger: 'blur',
      validator: async (rule, value) => {
        const response = await fetch(`/api/check-email?email=${value}`)
        const data = await response.json()
        if (!data.available) throw new Error('Этот email уже зарегистрирован')
        return true
      }
    }
  ],
  password: [
    {
      required: true,
      trigger: 'blur',
      validator: (rule, value) => value.length >= 8 || new Error('Пароль должен быть не менее 8 символов')
    },
    {
      trigger: 'blur',
      validator: (rule, value) => /\d/.test(value) || new Error('Пароль должен содержать хотя бы одну цифру')
    }
  ]
}

const submitForm = () => {
  formRef.value.validate().then(() => {
    console.log('Форма валидна', form.value)
  }).catch((errors) => {
    console.log('Ошибки валидации', errors)
  })
}
</script>

В этом примере каждое поле имеет свои кастомные правила, включая асинхронную проверку email и последовательные проверки пароля, что демонстрирует гибкость Naive UI при построении форм с сложной логикой валидации.

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