Основы схемы
Справочник FieldDefinition
interface FieldDefinition {
// ─── Обязательное ────────────────────────────────────────────────────────
type:
| 'text'
| 'number'
| 'email'
| 'select'
| 'checkbox'
| 'radio'
| 'textarea'
| 'date'
| 'array'
| 'group'
| 'file'
/** Плоский dot-path ключ в объекте значений, например "address.city" */
name: string
// ─── Отображение ──────────────────────────────────────────────────────────
label?: string
placeholder?: string
// ─── Начальное значение ───────────────────────────────────────────────────
/** Статическое значение или функция, вызываемая при инициализации с уже разрешёнными частичными значениями */
defaultValue?: unknown | ((values: Record<string, unknown>) => unknown)
// ─── Ограничения ──────────────────────────────────────────────────────────
required?: boolean
disabled?: boolean | ((values: Record<string, unknown>) => boolean)
/** Булево значение, функция или строковое выражение, вычисляемое над живыми значениями */
visible?: boolean | string | ((values: Record<string, unknown>) => boolean)
// ─── Валидация ────────────────────────────────────────────────────────────
validators?: ValidatorFn[]
asyncValidators?: AsyncValidatorFn[]
// ─── Маскирование ─────────────────────────────────────────────────────────
mask?: string | MaskConfig
// ─── опции select / radio ─────────────────────────────────────────────────
/** Статический массив, синхронная функция или асинхронная функция */
options?:
| FieldOption[]
| ((values: Record<string, unknown>) => FieldOption[])
| ((values: Record<string, unknown>) => Promise<FieldOption[]>)
/** Имена полей, изменение значений которых запускает повторную загрузку асинхронных options */
optionsDeps?: string[]
// ─── group / array ────────────────────────────────────────────────────────
fields?: FieldDefinition[]
// ─── transform / parse ────────────────────────────────────────────────────
/** Применяется при каждом вызове setField — используйте для trim, приведения типов, форматирования */
transform?: (value: unknown, values: Record<string, unknown>) => unknown
/** Применяется во время отправки для формирования финального значения payload */
parse?: (raw: unknown) => unknown
// ─── Кастомный компонент ──────────────────────────────────────────────────
/** Компонент Vue или зарегистрированное имя; получает FormFieldProps */
component?: Component | string
// ─── Опции поля file ──────────────────────────────────────────────────────
accept?: string // передаётся в <input accept>
multiple?: boolean
maxSize?: number // байты (информационно; для принудительного ограничения используйте валидатор fileSize)
maxFiles?: number // информационно; для принудительного ограничения используйте валидатор fileCount
}Composable useForm
import { useForm } from '@macrulez/vue-form-schema'
const form = useForm(config)Конфигурация
| Свойство | Тип | По умолчанию | Описание |
|---|---|---|---|
schema | FieldDefinition[] | JSONSchema | — | Определения полей |
initialValues | Partial<T> | {} | Начальные значения (переопределяют defaultValue полей) |
validateOn | 'input' | 'blur' | 'submit' | 'eager' | 'blur' | Когда срабатывает валидация |
validateMode | 'first' | 'all' | 'first' | Возвращать только первую ошибку или все |
clearOnHide | boolean | false | Сбрасывать значение поля, когда оно скрывается |
onSubmit | (values: T) => void | Promise<void> | — | Вызывается после успешной валидации |
persist | false | 'session' | 'local' | false | Сохранять значения в sessionStorage / localStorage |
persistKey | string | авто | Префикс ключа хранилища |
debug | boolean | false | Логировать изменения состояния в console.group |
Возвращаемое значение
| Свойство | Тип | Описание |
|---|---|---|
fields | ComputedRef<FieldDefinition[]> | Поля после вычисления условий |
values | Ref<T> | Текущие значения формы |
errors | Ref<Record<string, string[]>> | Ошибки валидации по имени поля |
touched | Ref<Record<string, boolean>> | Поля, у которых уже был blur |
optionsLoading | Ref<Record<string, boolean>> | Состояние загрузки асинхронных options по каждому полю |
isDirty | ComputedRef<boolean> | true, когда значения отличаются от начального состояния |
isValid | ComputedRef<boolean> | true, когда все видимые поля проходят валидацию |
isSubmitting | Ref<boolean> | true, пока выполняется onSubmit |
submit() | () => Promise<void> | Отметить все поля, провалидировать, вызвать onSubmit |
reset(values?) | — | Восстановить начальное состояние или задать новые значения |
setField(path, value) | — | Установить значение по dot-path |
getField(path) | — | Прочитать значение по dot-path |
validateOn: 'eager'
При 'eager' валидация запускается при вводе — но только после того, как поле хотя бы раз потеряло фокус. Это избавляет от показа ошибок, пока пользователь ещё вводит значение впервые.
Форматы схемы
Массив FieldDefinition
import type { FieldDefinition } from '@macrulez/vue-form-schema'
const schema: FieldDefinition[] = [{ type: 'text', name: 'username', required: true }]
useForm({ schema })JSON-схема
Сериализуемый формат для схем, управляемых сервером. Передайте напрямую в useForm (автоопределение) либо вызовите parseJSON явно.
const raw = [
{
type: 'text',
name: 'username',
default: '',
required: true,
validators: [
{ rule: 'minLength', value: 3, message: 'At least 3 characters' },
{ rule: 'maxLength', value: 20 },
],
},
]
useForm({ schema: raw }) // автоопределение
// или
import { parseJSON } from '@macrulez/vue-form-schema'
const fields = parseJSON(raw)Поддерживаемые правила JSON-валидатора: required, minLength, maxLength, min, max, pattern, email, url. Все принимают опциональное переопределение message.
Zod
import { z } from 'zod'
import { parseZod } from '@macrulez/vue-form-schema/zod'
const schema = z.object({
name: z.string().min(2).describe('Full name'),
age: z.number().min(0).optional(),
email: z.string().email(),
role: z.enum(['admin', 'user']),
})
const fields = parseZod(schema)
const { values } = useForm({ schema: fields })
// values.value.name — string, values.value.age — number | undefined, ...
// — выводится автоматически из `schema` через z.infer<typeof schema>,
// useForm<Values>(...) не нужен.Маппинг Zod → тип поля: z.string() → text, z.number() → number, z.boolean() → checkbox, z.enum() → select, z.array() → array, z.object() → group. Используйте .describe('label'), чтобы задать метку поля.
parseZod также принимает корневую схему z.discriminatedUnion(key, [...]) — см. Дискриминированные схемы.
Yup
import { object, string, number } from 'yup'
import { parseYup } from '@macrulez/vue-form-schema/yup'
const schema = object({
name: string().required().label('Full name'),
email: string().email().required(),
age: number().min(0).optional(),
})
const fields = parseYup(schema)
const { values } = useForm({ schema: fields })
// values.value типизирован из InferType<typeof schema> автоматическиValibot
import * as v from 'valibot'
import { parseValibot } from '@macrulez/vue-form-schema/valibot'
const schema = v.object({
name: v.pipe(v.string(), v.minLength(2)),
email: v.pipe(v.string(), v.email()),
age: v.optional(v.number()),
role: v.picklist(['admin', 'user']),
})
const fields = parseValibot(schema)
const { values } = useForm({ schema: fields })
// values.value типизирован из v.InferOutput<typeof schema> автоматическиМаппинг Valibot → тип поля: v.string() → text, v.number() → number, v.boolean() → checkbox, v.picklist() / v.enum() → select, v.array() → array, v.object() → group. v.pipe(v.string(), v.email()) → type: 'email'. v.optional() / v.nullable() → required: false.
parseValibot также принимает корневую схему v.variant(key, [...]) — см. Дискриминированные схемы.
OpenAPI / стандартная JSON Schema
В отличие от parseJSON (собственный упрощённый формат этой библиотеки на основе правил), parseJSONSchema / parseOpenAPI принимают настоящую JSON Schema — ту, что ваш backend уже выдаёт через OpenAPI/Swagger — так что слой трансляции между спецификацией API и формой не нужен.
import { parseOpenAPI } from '@macrulez/vue-form-schema/openapi'
// openapiDocument — ваш полный документ OpenAPI (например, полученный с /openapi.json)
const fields = parseOpenAPI(openapiDocument, { path: '/users', method: 'post' })
// или по JSON pointer в components.schemas:
const fields2 = parseOpenAPI(openapiDocument, '#/components/schemas/User')
const { values } = useForm({ schema: fields })Либо на отдельном объекте JSON Schema, без обёртки OpenAPI:
import { parseJSONSchema } from '@macrulez/vue-form-schema/openapi'
const fields = parseJSONSchema({
type: 'object',
properties: {
name: { type: 'string', minLength: 2 },
age: { type: 'integer', minimum: 0 },
role: { type: 'string', enum: ['admin', 'user'] },
},
required: ['name', 'role'],
} as const)
const { values } = useForm({ schema: fields })
// values.value.role типизирован как 'admin' | 'user' — выведено из схемы с `as const`Поддерживаемое подмножество: type (object / string / number / integer / boolean / array, включая массив типов вроде ['string', 'null']), properties + required, items (схемы элементов массива — у object-элементов их properties отображаются в безымянные строковые fields, по соглашениям для полей-массивов), enum / const → select, format (email, date / date-time, uri/url), minLength/maxLength/minimum/maximum/pattern и локальные $ref (#/..., разрешаются относительно документа, переданного как rootDocument, либо относительно самой схемы для самодостаточных $defs).
Не поддерживается (намеренно — полная JSON Schema это огромная спецификация): oneOf / anyOf / allOf, additionalProperties, patternProperties, удалённый/внешний $ref, tuple-форма items. Свойства, использующие их, разбираются как обычное поле text без неподдерживаемого ограничения, а не выбрасывают исключение.
Возвращаемый тип parseJSONSchema несёт наилучшим образом выведенный тип значения из литерала схемы (нужен as const, как и у defineSchema). parseOpenAPI не может вывести тип статически (форма извлечённой схемы зависит от аргумента path/selector во время выполнения) — передайте явный типовой аргумент, если вы уже генерируете его из своего документа OpenAPI, например через openapi-typescript: parseOpenAPI<CreateUserRequest>(document, '#/components/schemas/User').