Стандарты
Формы и валидация
Стандарт валидации форм с использованием Nuxt UI 4 и Zod.
Стандарт валидации форм с использованием Nuxt UI 4 и Zod.
Структура формы (Nuxt UI 4)
В Nuxt UI 4 для форм используются:
UForm— контейнер формы с валидацией (Zod schema)UFormField— обёртка для полей с label и error (заменяет UFormGroup из v3)UInput,UTextarea,USelect— инпуты
<template>
<UForm :schema="schema" :state="state" @submit="onSubmit">
<UFormField label="Email" name="email">
<UInput v-model="state.email" placeholder="email@example.com" />
</UFormField>
<UFormField label="Пароль" name="password">
<UInput v-model="state.password" type="password" />
</UFormField>
<UFormField label="Телефон" name="phone">
<UInput v-model="state.phone" placeholder="+7 (___) ___-__-__" />
</UFormField>
<UButton type="submit" :loading="loading">
Сохранить
</UButton>
</UForm>
</template>
<script setup lang="ts">
import { z } from 'zod'
import type { FormSubmitEvent } from '#ui/types'
const loading = ref(false)
const state = reactive({
email: '',
password: '',
phone: '',
})
const schema = z.object({
email: z.string().email('Некорректный email'),
password: z.string().min(8, 'Минимум 8 символов'),
phone: z.string().regex(/^\+7 \(\d{3}\) \d{3}-\d{2}-\d{2}$/, 'Некорректный формат'),
})
type Schema = z.output<typeof schema>
async function onSubmit(event: FormSubmitEvent<Schema>) {
loading.value = true
try {
await api.post('/users', event.data)
// success
} finally {
loading.value = false
}
}
</script>
Nuxt UI 4 vs UI 3: В Nuxt UI 4 компонент
UFormGroup переименован в UFormField. Используйте UFormField для всех новых проектов.Валидация с Zod
Базовые правила
import { z } from 'zod'
const schema = z.object({
// Обязательное поле
name: z.string().min(1, 'Введите имя'),
// Email
email: z.string().email('Некорректный email'),
// Минимальная длина
password: z.string().min(8, 'Минимум 8 символов'),
// Паттерн
bin: z.string().regex(/^\d{12}$/, 'БИН должен содержать 12 цифр'),
// Опциональное
phone: z.string().optional(),
// Число
age: z.number().min(18, 'Минимум 18 лет'),
})
Типовые схемы
// Телефон KZ
const phoneSchema = z.string().regex(
/^\+7 \(\d{3}\) \d{3}-\d{2}-\d{2}$/,
'Формат: +7 (XXX) XXX-XX-XX'
)
// БИН/ИИН
const binSchema = z.string().regex(/^\d{12}$/, '12 цифр')
// Email
const emailSchema = z.string().email('Некорректный email')
// Пароль
const passwordSchema = z.string()
.min(8, 'Минимум 8 символов')
.regex(/[A-Z]/, 'Минимум одна заглавная буква')
.regex(/[0-9]/, 'Минимум одна цифра')
Компоненты формы
UFormField (Nuxt UI 4)
<UFormField
label="Email"
name="email"
description="Используется для входа"
hint="Необязательно"
required
>
<UInput v-model="state.email" />
</UFormField>
| Prop | Описание |
|---|---|
label | Лейбл поля |
name | Имя поля (для валидации, должно соответствовать ключу в schema) |
description | Подсказка под полем |
hint | Текст справа от лейбла |
required | Показать звёздочку |
error | Ручная установка ошибки (для серверных ошибок) |
UInput с маской
<script setup>
import { vMaska } from 'maska'
</script>
<template>
<UInput
v-model="state.phone"
v-maska="'+7 (###) ###-##-##'"
placeholder="+7 (___) ___-__-__"
/>
</template>
Типовые маски
| Поле | Маска |
|---|---|
| Телефон KZ | +7 (###) ###-##-## |
| БИН/ИИН | ############ |
| Дата | ##.##.#### |
| Банковский счёт | KZ## #### #### #### #### |
Состояния формы
Loading
<UButton type="submit" :loading="loading">
Сохранить
</UButton>
Disabled поля
<UInput v-model="state.email" :disabled="isReadonly" />
Ошибки с сервера
<script setup>
const serverErrors = ref<Record<string, string>>({})
async function onSubmit(event) {
try {
await api.post('/users', event.data)
} catch (error) {
if (error.status === 422) {
serverErrors.value = error.details
}
}
}
</script>
<template>
<UFormField label="Email" name="email" :error="serverErrors.email">
<UInput v-model="state.email" />
</UFormField>
</template>
Паттерн формы в модальном окне
<template>
<UModal v-model="isOpen">
<UCard>
<template #header>
<h3>Создать пользователя</h3>
</template>
<UForm :schema="schema" :state="state" @submit="onSubmit">
<UFormField label="Имя" name="name">
<UInput v-model="state.name" />
</UFormField>
<template #footer>
<div class="flex justify-end gap-2">
<UButton variant="ghost" @click="isOpen = false">
Отмена
</UButton>
<UButton type="submit" :loading="loading">
Создать
</UButton>
</div>
</template>
</UForm>
</UCard>
</UModal>
</template>
Чек-лист
- Используется UForm с schema (Zod)
- Каждое поле в UFormField с label и name
- Сообщения об ошибках контекстные (на русском)
- Телефоны и БИН/ИИН имеют маски
- Кнопка submit имеет loading состояние
- Обработаны ошибки сервера