Валидация
Встроенные валидаторы
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 сработал.
Если нужно реактивно узнавать, ждёт ли ещё завершения хотя бы один асинхронный валидатор, читайте form.isValidating — form.isValid уже учитывает его (возвращает false, пока isValidating равно true), так что isValidating в основном полезен, чтобы показать в интерфейсе отдельное состояние «идёт проверка…».
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}`,
}