Skip to content

Компоненты ​

Компоненты делают то же, что composable, но без кода в <script>: форму достаточно создать через useForm, остальное — в шаблоне. Поля находят форму сами — через <TvForm> выше по дереву.

vue
<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:

vue
<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 }.

vue
<TvError name="email" v-slot="{ message, id }">
  <p :id="id" class="text-red-600">⚠ {{ message }}</p>
</TvError>

TvFieldArray ​

Список полей — слот получает всё, что возвращает useFieldArray: fields, append, remove, move…

vue
<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>

См. также ​