Компоненты
Компоненты делают то же, что composable, но без кода в <script>: форму достаточно создать через useForm, остальное — в шаблоне. Поля находят форму сами — через <TvForm> выше по дереву.
<script setup lang="ts">
import { useForm, TvForm, TvField, TvError } from 'tinyval-vue';
const form = useForm(Signup, { initial: { name: '', email: '', plan: 'free', terms: false } });
const save = (data) => api.post('/signup', data); // 422 from Laravel is shown at the fields automatically
</script>
<template>
<TvForm :form="form" @submit="save">
<TvField name="name" placeholder="Name" />
<TvError name="name" />
<TvField name="email" type="email" />
<TvError name="email" />
<TvField name="plan" type="radio" value="free" /> Free
<TvField name="plan" type="radio" value="pro" /> Pro
<TvField name="terms" type="checkbox" /> I accept the terms
<button :disabled="form.submitting">Sign up</button>
</TvForm>
</template>TvForm
Рендерит <form novalidate>, отправляет через handleSubmit и передаёт форму вложенным полям.
| Prop / событие | Описание |
|---|---|
form | Форма из useForm. Обязателен |
@submit | Вызывается с проверенными данными. Если вернёт Promise — form.submitting будет true, пока он не завершится; отклонение с ответом 422 разложится по полям |
@invalid | Вызывается с ошибками, если форма не прошла проверку |
Слот по умолчанию получает { form }.
TvField
Поле, связанное с формой по name. Остальные атрибуты (placeholder, class, autocomplete…) передаются на элемент.
| Prop | Описание |
|---|---|
name | Путь поля: email, address.city, items.0.title |
as | 'input' (по умолчанию), 'textarea', 'select' или компонент UI-библиотеки с v-model |
type | Тип <input>: checkbox, radio, file и любые другие |
value | Значение варианта для radio и группы чекбоксов |
multiple | Несколько файлов или вариантов <select> |
form | Форма явно — если <TvForm> выше по дереву нет |
| Поле | Значение в форме |
|---|---|
<TvField name="bio" as="textarea" /> | строка |
<TvField name="terms" type="checkbox" /> | true / false |
<TvField name="tags" type="checkbox" value="js" /> | массив отмеченных value |
<TvField name="plan" type="radio" value="pro" /> | value выбранной кнопки |
<TvField name="city" as="select"> + <option> | значение выбранного варианта |
<TvField name="langs" as="select" multiple> | массив значений |
<TvField name="avatar" type="file" /> | File или null; с multiple — массив |
<TvField name="email" :as="ElInput" /> | v-model компонента |
Полностью свою разметку можно отрисовать через слот — он получает field (для v-bind), value, setValue, error, errorId, touched, dirty:
<TvField name="email" v-slot="{ field, error, errorId }">
<el-form-item :error="error">
<el-input v-bind="field" />
</el-form-item>
</TvField>TvError
Текст ошибки поля, если её пора показать. Рендерит <span role="alert"> с id, на который ссылается aria-describedby поля.
| Prop | Описание |
|---|---|
name | Путь поля |
as | Тег, по умолчанию span |
form | Форма явно — если <TvForm> выше по дереву нет |
Своя разметка — через слот { message, id }.
<TvError name="email" v-slot="{ message, id }">
<p :id="id" class="text-red-600">⚠ {{ message }}</p>
</TvError>TvFieldArray
Список полей — слот получает всё, что возвращает useFieldArray: fields, append, remove, move…
<TvFieldArray name="items" v-slot="{ fields, append, remove }">
<div v-for="f in fields" :key="f.key">
<TvField :name="`${f.path}.title`" />
<TvError :name="`${f.path}.title`" />
<button type="button" @click="remove(f.index)">×</button>
</div>
<button type="button" @click="append({ title: '' })">+</button>
</TvFieldArray>См. также
- useField — то же для своих компонентов полей
- UI-библиотеки