Валидация
Встроенные валидаторы
import {
required,
minLength,
maxLength,
min,
max,
pattern,
email,
url,
sameAs,
fileType,
fileSize,
fileCount,
} from '@macrulez/vue-form-schema'| Функция | Описание |
|---|---|
required | Проваливается для null, undefined, '' или пустого массива |
minLength(n, msg?) | Минимальная длина строки или массива |
maxLength(n, msg?) | Максимальная длина строки или массива |
min(n, msg?) | Числовой минимум |
max(n, msg?) | Числовой максимум |
pattern(re, msg?) | Совпадение с regex |
email | Базовый формат email |
url | Корректный URL (new URL()) |
sameAs(field, msg?) | Значение должно совпадать с другим полем |
fileType(types[], msg?) | Белый список MIME-типа или расширения файла |
fileSize(bytes, msg?) | Максимальный размер файла |
fileCount(n, msg?) | Максимальное количество файлов |
Кастомные валидаторы
Синхронные
import type { ValidatorFn } from '@macrulez/vue-form-schema'
const noSpaces: ValidatorFn = (value) =>
typeof value === 'string' && value.includes(' ') ? 'No spaces allowed' : nullАсинхронные
Асинхронные валидаторы дебаунсятся (300 мс), пока пользователь вводит текст или переводит фокус с поля. Ошибки объединяются в errors после разрешения. При submit() асинхронные валидаторы запускаются немедленно (минуя дебаунс) и ожидаются, так что незавершённая проверка (например, «имя пользователя занято») блокирует отправку, а не разрешается уже после того, как onSubmit сработал.
import type { AsyncValidatorFn } from '@macrulez/vue-form-schema'
const uniqueUsername: AsyncValidatorFn = async (value) => {
const { taken } = await fetch(`/api/check?q=${value}`).then((r) => r.json())
return taken ? 'Username is taken' : null
}Несколько ошибок на поле (validateMode)
useForm({
schema,
validateMode: 'all', // собирать все ошибки для поля (по умолчанию: 'first')
})Кросс-полевая валидация
Используйте sameAs для подтверждения пароля или напишите свой валидатор — все валидаторы получают allValues вторым аргументом.
import { sameAs } from '@macrulez/vue-form-schema'
const schema: FieldDefinition[] = [
{ type: 'text', name: 'password', label: 'Password', required: true },
{
type: 'text',
name: 'confirm',
label: 'Confirm password',
validators: [sameAs('password', 'Passwords must match')],
},
]Условные поля
visible и disabled могут быть булевым значением, реактивной функцией или безопасным строковым выражением.
const schema: FieldDefinition[] = [
{ type: 'checkbox', name: 'hasCompany', label: 'I represent a company' },
{
type: 'text',
name: 'companyName',
label: 'Company name',
visible: (values) => values['hasCompany'] === true,
required: true,
},
// строковое выражение — имеет доступ к переменной `values`
{
type: 'select',
name: 'drink',
label: 'Drink',
visible: 'values.age >= 18',
options: [
{ label: 'Beer', value: 'beer' },
{ label: 'Water', value: 'water' },
],
},
]Задайте clearOnHide: true в useForm, чтобы автоматически сбрасывать значение скрытого поля.
Динамические опции
options может быть статическим массивом, синхронной функцией или асинхронной функцией.
// Синхронно — переоценивается при каждом изменении values
{
type: 'select',
name: 'city',
options: (values) => citiesByCountry[values['country'] as string] ?? [],
}
// Асинхронно — загружается при монтировании и повторно при изменении optionsDeps
{
type: 'select',
name: 'framework',
optionsDeps: ['language'],
options: async (values) => {
const res = await fetch(`/api/frameworks?lang=${values['language']}`)
return res.json()
},
}Пока идёт загрузка, optionsLoading.value['framework'] равно true, и select отключён в FormRenderer. Доступ к состоянию загрузки напрямую — через form.optionsLoading.
Вычисляемый defaultValue
defaultValue также может быть функцией, вычисляемой при инициализации формы с уже разрешёнными частичными значениями в качестве контекста:
{
type: 'text',
name: 'displayName',
defaultValue: (values) => `${values.firstName} ${values.lastName}`,
}