Form
使用法
フォームコンポーネントを使用して、標準Schemaをサポートする検証ライブラリ( Valibot、Zod、 Regle、Yupなど)を使用してフォームデータを検証します。JoiまたはSuperstructまたは独自の検証ロジック。
FormFieldコンポーネントと連携して、フォーム要素に関するエラーメッセージを自動的に表示します。
Schema検証
小道具は2つ必要です。
state—フォームの状態を保持するリアクティブオブジェクト。schema—任意の標準SchemaまたはSuperstruct。
<script setup lang="ts">
import * as v from 'valibot'
import type { FormSubmitEvent } from '@nuxt/ui'
const schema = v.object({
email: v.pipe(v.string(), v.email('Invalid email')),
password: v.pipe(v.string(), v.minLength(8, 'Must be at least 8 characters'))
})
type Schema = v.InferOutput<typeof schema>
const state = reactive({
email: '',
password: ''
})
const toast = useToast()
async function onSubmit(event: FormSubmitEvent<Schema>) {
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' })
console.log(event.data)
}
</script>
<template>
<UForm :schema="schema" :state="state" class="space-y-4" @submit="onSubmit">
<UFormField label="Email" name="email">
<UInput v-model="state.email" />
</UFormField>
<UFormField label="Password" name="password">
<UInput v-model="state.password" type="password" />
</UFormField>
<UButton type="submit">
Submit
</UButton>
</UForm>
</template>
<script setup lang="ts">
import { reactive } from 'vue'
import * as v from 'valibot'
import type { FormSubmitEvent } from '@nuxt/ui'
const schema = v.object({
email: v.pipe(v.string(), v.email('Invalid email')),
password: v.pipe(v.string(), v.minLength(8, 'Must be at least 8 characters'))
})
type Schema = v.InferOutput<typeof schema>
const state = reactive({
email: '',
password: ''
})
const toast = useToast()
async function onSubmit(event: FormSubmitEvent<Schema>) {
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' })
console.log(event.data)
}
</script>
<template>
<UForm :schema="schema" :state="state" class="space-y-4" @submit="onSubmit">
<UFormField label="Email" name="email">
<UInput v-model="state.email" />
</UFormField>
<UFormField label="Password" name="password">
<UInput v-model="state.password" type="password" />
</UFormField>
<UButton type="submit">
Submit
</UButton>
</UForm>
</template>
<script setup lang="ts">
import * as z from 'zod'
import type { FormSubmitEvent } from '@nuxt/ui'
const schema = z.object({
email: z.email('Invalid email'),
password: z.string('Password is required').min(8, 'Must be at least 8 characters')
})
type Schema = z.output<typeof schema>
const state = reactive<Partial<Schema>>({
email: undefined,
password: undefined
})
const toast = useToast()
async function onSubmit(event: FormSubmitEvent<Schema>) {
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' })
console.log(event.data)
}
</script>
<template>
<UForm :schema="schema" :state="state" class="space-y-4" @submit="onSubmit">
<UFormField label="Email" name="email">
<UInput v-model="state.email" />
</UFormField>
<UFormField label="Password" name="password">
<UInput v-model="state.password" type="password" />
</UFormField>
<UButton type="submit">
Submit
</UButton>
</UForm>
</template>
<script setup lang="ts">
import { reactive } from 'vue'
import * as z from 'zod'
import type { FormSubmitEvent } from '@nuxt/ui'
const schema = z.object({
email: z.email('Invalid email'),
password: z.string('Password is required').min(8, 'Must be at least 8 characters')
})
type Schema = z.output<typeof schema>
const state = reactive<Partial<Schema>>({
email: undefined,
password: undefined
})
const toast = useToast()
async function onSubmit(event: FormSubmitEvent<Schema>) {
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' })
console.log(event.data)
}
</script>
<template>
<UForm :schema="schema" :state="state" class="space-y-4" @submit="onSubmit">
<UFormField label="Email" name="email">
<UInput v-model="state.email" />
</UFormField>
<UFormField label="Password" name="password">
<UInput v-model="state.password" type="password" />
</UFormField>
<UButton type="submit">
Submit
</UButton>
</UForm>
</template>
<script setup lang="ts">
import { useRegle, type InferInput } from '@regle/core'
import { required, email, minLength, withMessage } from '@regle/rules'
import type { FormSubmitEvent } from '@nuxt/ui'
const { r$ } = useRegle({ email: '', password: '' }, {
email: { required, email: withMessage(email, 'Invalid email') },
password: { required, minLength: withMessage(minLength(8), 'Must be at least 8 characters') }
})
type Schema = InferInput<typeof r$>
const toast = useToast()
async function onSubmit(event: FormSubmitEvent<Schema>) {
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' })
console.log(event.data)
}
</script>
<template>
<UForm :schema="r$" :state="r$.$value" class="space-y-4" @submit="onSubmit">
<UFormField label="Email" name="email">
<UInput v-model="r$.$value.email" />
</UFormField>
<UFormField label="Password" name="password">
<UInput v-model="r$.$value.password" type="password" />
</UFormField>
<UButton type="submit">
Submit
</UButton>
</UForm>
</template>
<script setup lang="ts">
import { object, string } from 'yup'
import type { InferType } from 'yup'
import type { FormSubmitEvent } from '@nuxt/ui'
const schema = object({
email: string().email('Invalid email').required('Required'),
password: string()
.min(8, 'Must be at least 8 characters')
.required('Required')
})
type Schema = InferType<typeof schema>
const state = reactive({
email: undefined,
password: undefined
})
const toast = useToast()
async function onSubmit(event: FormSubmitEvent<Schema>) {
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' })
console.log(event.data)
}
</script>
<template>
<UForm :schema="schema" :state="state" class="space-y-4" @submit="onSubmit">
<UFormField label="Email" name="email">
<UInput v-model="state.email" />
</UFormField>
<UFormField label="Password" name="password">
<UInput v-model="state.password" type="password" />
</UFormField>
<UButton type="submit">
Submit
</UButton>
</UForm>
</template>
<script setup lang="ts">
import { reactive } from 'vue'
import { object, string } from 'yup'
import type { InferType } from 'yup'
import type { FormSubmitEvent } from '@nuxt/ui'
const schema = object({
email: string().email('Invalid email').required('Required'),
password: string()
.min(8, 'Must be at least 8 characters')
.required('Required')
})
type Schema = InferType<typeof schema>
const state = reactive({
email: undefined,
password: undefined
})
const toast = useToast()
async function onSubmit(event: FormSubmitEvent<Schema>) {
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' })
console.log(event.data)
}
</script>
<template>
<UForm :schema="schema" :state="state" class="space-y-4" @submit="onSubmit">
<UFormField label="Email" name="email">
<UInput v-model="state.email" />
</UFormField>
<UFormField label="Password" name="password">
<UInput v-model="state.password" type="password" />
</UFormField>
<UButton type="submit">
Submit
</UButton>
</UForm>
</template>
<script setup lang="ts">
import Joi from 'joi'
import type { FormSubmitEvent } from '@nuxt/ui'
const schema = Joi.object({
email: Joi.string().required(),
password: Joi.string()
.min(8)
.required()
})
const state = reactive({
email: undefined,
password: undefined
})
const toast = useToast()
async function onSubmit(event: FormSubmitEvent<typeof state>) {
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' })
console.log(event.data)
}
</script>
<template>
<UForm :schema="schema" :state="state" class="space-y-4" @submit="onSubmit">
<UFormField label="Email" name="email">
<UInput v-model="state.email" />
</UFormField>
<UFormField label="Password" name="password">
<UInput v-model="state.password" type="password" />
</UFormField>
<UButton type="submit">
Submit
</UButton>
</UForm>
</template>
<script setup lang="ts">
import { reactive } from 'vue'
import Joi from 'joi'
import type { FormSubmitEvent } from '@nuxt/ui'
const schema = Joi.object({
email: Joi.string().required(),
password: Joi.string()
.min(8)
.required()
})
const state = reactive({
email: undefined,
password: undefined
})
const toast = useToast()
async function onSubmit(event: FormSubmitEvent<typeof state>) {
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' })
console.log(event.data)
}
</script>
<template>
<UForm :schema="schema" :state="state" class="space-y-4" @submit="onSubmit">
<UFormField label="Email" name="email">
<UInput v-model="state.email" />
</UFormField>
<UFormField label="Password" name="password">
<UInput v-model="state.password" type="password" />
</UFormField>
<UButton type="submit">
Submit
</UButton>
</UForm>
</template>
<script setup lang="ts">
import { object, string, nonempty, refine } from 'superstruct'
import type { Infer } from 'superstruct'
import type { FormSubmitEvent } from '@nuxt/ui'
const schema = object({
email: nonempty(string()),
password: refine(string(), 'Password', (value) => {
if (value.length >= 8) return true
return 'Must be at least 8 characters'
})
})
const state = reactive({
email: '',
password: ''
})
type Schema = Infer<typeof schema>
async function onSubmit(event: FormSubmitEvent<Schema>) {
console.log(event.data)
}
</script>
<template>
<UForm :schema="schema" :state="state" class="space-y-4" @submit="onSubmit">
<UFormField label="Email" name="email">
<UInput v-model="state.email" />
</UFormField>
<UFormField label="Password" name="password">
<UInput v-model="state.password" type="password" />
</UFormField>
<UButton type="submit">
Submit
</UButton>
</UForm>
</template>
<script setup lang="ts">
import { reactive } from 'vue'
import { object, string, nonempty, refine } from 'superstruct'
import type { Infer } from 'superstruct'
import type { FormSubmitEvent } from '@nuxt/ui'
const schema = object({
email: nonempty(string()),
password: refine(string(), 'Password', (value) => {
if (value.length >= 8) return true
return 'Must be at least 8 characters'
})
})
const state = reactive({
email: '',
password: ''
})
type Schema = Infer<typeof schema>
async function onSubmit(event: FormSubmitEvent<Schema>) {
console.log(event.data)
}
</script>
<template>
<UForm :schema="schema" :state="state" class="space-y-4" @submit="onSubmit">
<UFormField label="Email" name="email">
<UInput v-model="state.email" />
</UFormField>
<UFormField label="Password" name="password">
<UInput v-model="state.password" type="password" />
</UFormField>
<UButton type="submit">
Submit
</UButton>
</UForm>
</template>
カスタム検証
validateプロパティを使用して、独自の検証ロジックを適用します。
バリデーション関数は、以下の属性を持つエラーのリストを返す必要があります。
message—表示するエラーメッセージ。name—エラーを送信するFormFieldのname。
schemaプロパティと一緒に使用して、複雑なユースケースを処理できます。<script setup lang="ts">
import type { FormError, FormSubmitEvent } from '@nuxt/ui'
const state = reactive({
email: undefined,
password: undefined
})
type Schema = typeof state
function validate(state: Partial<Schema>): FormError[] {
const errors = []
if (!state.email) errors.push({ name: 'email', message: 'Required' })
if (!state.password) errors.push({ name: 'password', message: 'Required' })
return errors
}
const toast = useToast()
async function onSubmit(event: FormSubmitEvent<Schema>) {
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' })
console.log(event.data)
}
</script>
<template>
<UForm :validate="validate" :state="state" class="space-y-4" @submit="onSubmit">
<UFormField label="Email" name="email">
<UInput v-model="state.email" />
</UFormField>
<UFormField label="Password" name="password">
<UInput v-model="state.password" type="password" />
</UFormField>
<UButton type="submit">
Submit
</UButton>
</UForm>
</template>
<script setup lang="ts">
import { reactive } from 'vue'
import type { FormError, FormSubmitEvent } from '@nuxt/ui'
const state = reactive({
email: undefined,
password: undefined
})
type Schema = typeof state
function validate(state: Partial<Schema>): FormError[] {
const errors = []
if (!state.email) errors.push({ name: 'email', message: 'Required' })
if (!state.password) errors.push({ name: 'password', message: 'Required' })
return errors
}
const toast = useToast()
async function onSubmit(event: FormSubmitEvent<Schema>) {
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' })
console.log(event.data)
}
</script>
<template>
<UForm :validate="validate" :state="state" class="space-y-4" @submit="onSubmit">
<UFormField label="Email" name="email">
<UInput v-model="state.email" />
</UFormField>
<UFormField label="Password" name="password">
<UInput v-model="state.password" type="password" />
</UFormField>
<UButton type="submit">
Submit
</UButton>
</UForm>
</template>
Errorレポート
エラーは、対応するFormFieldのnameプロパティを使用してマッチングされます。emailフィールドのエラーは<FormField name="email">で表示されます。
ネストされたフィールドはドット表記でマッチングされます。<FormField name="user.email">には{ user: z.object({ email: z.string() }) }のようなスキーマが適用されます。
tags.0tags.1、nameだけでは<FormField name="tags">にはマッチしません。error-patternプロパティに/^tags\..+/のような正規表現を加えて使用してください。これは特にInputTagsのようなコンポーネントに便利です。<script setup lang="ts">
import * as z from 'zod'
import type { FormSubmitEvent } from '@nuxt/ui'
const schema = z.object({
email: z.email('Invalid email'),
tags: z.array(z.string().regex(/^[a-z-]+$/, 'Lowercase letters and dashes only')).min(1, 'Please add at least one tag')
})
type Schema = z.output<typeof schema>
const state = reactive<Partial<Schema>>({
email: undefined,
tags: []
})
const toast = useToast()
async function onSubmit(event: FormSubmitEvent<Schema>) {
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' })
console.log(event.data)
}
</script>
<template>
<UForm :schema="schema" :state="state" class="space-y-4" @submit="onSubmit">
<UFormField label="Email" name="email">
<UInput v-model="state.email" />
</UFormField>
<UFormField label="Tags" name="tags" :error-pattern="/^tags\..+/">
<UInputTags v-model="state.tags" />
</UFormField>
<UButton type="submit">
Submit
</UButton>
</UForm>
</template>
<script setup lang="ts">
import { reactive } from 'vue'
import * as z from 'zod'
import type { FormSubmitEvent } from '@nuxt/ui'
const schema = z.object({
email: z.email('Invalid email'),
tags: z.array(z.string().regex(/^[a-z-]+$/, 'Lowercase letters and dashes only')).min(1, 'Please add at least one tag')
})
type Schema = z.output<typeof schema>
const state = reactive<Partial<Schema>>({
email: undefined,
tags: []
})
const toast = useToast()
async function onSubmit(event: FormSubmitEvent<Schema>) {
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' })
console.log(event.data)
}
</script>
<template>
<UForm :schema="schema" :state="state" class="space-y-4" @submit="onSubmit">
<UFormField label="Email" name="email">
<UInput v-model="state.email" />
</UFormField>
<UFormField label="Tags" name="tags" :error-pattern="/^tags\..+/">
<UInputTags v-model="state.tags" />
</UFormField>
<UButton type="submit">
Submit
</UButton>
</UForm>
</template>
Inputイベント
フォームコンポーネントは、入力がinput、change、またはblurイベントを出力すると自動的に検証をトリガします。
inputの検証は、eをタイプするとが行われます。changeのバリデーションは、xvalueにコミットすると発生します。blurの検証は、入力focusを失うと行われます。
validate-on propを使ってバリデーションのタイミングを制御できます。
useFormFieldコンポーザブルを使用して、独自のコンポーネント内でこれを実装できます。Errorイベント
エラーを処理するために@errorイベントをリッスンできます。このイベントはフォームが送信されたときにトリガーされ、次のフィールドを持つFormErrorオブジェクトの配列が含まれています。
id—入力のid。name—FormFieldのnamemessage—表示するエラーメッセージ。
フォームが送信された後にエラーのある最初のinput要素に焦点を当てる例を以下に示します:
<script setup lang="ts">
import type { FormError, FormErrorEvent, FormSubmitEvent } from '@nuxt/ui'
const state = reactive({
email: undefined,
password: undefined
})
type Schema = typeof state
function validate(state: Partial<Schema>): FormError[] {
const errors = []
if (!state.email) errors.push({ name: 'email', message: 'Required' })
if (!state.password) errors.push({ name: 'password', message: 'Required' })
return errors
}
const toast = useToast()
async function onSubmit(event: FormSubmitEvent<Schema>) {
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' })
console.log(event.data)
}
async function onError(event: FormErrorEvent) {
if (event?.errors?.[0]?.id) {
const element = document.getElementById(event.errors[0].id)
element?.focus()
element?.scrollIntoView({ behavior: 'smooth', block: 'center' })
}
}
</script>
<template>
<UForm :validate="validate" :state="state" class="space-y-4" @submit="onSubmit" @error="onError">
<UFormField label="Email" name="email">
<UInput v-model="state.email" />
</UFormField>
<UFormField label="Password" name="password">
<UInput v-model="state.password" type="password" />
</UFormField>
<UButton type="submit">
Submit
</UButton>
</UForm>
</template>
<script setup lang="ts">
import { reactive } from 'vue'
import type { FormError, FormErrorEvent, FormSubmitEvent } from '@nuxt/ui'
const state = reactive({
email: undefined,
password: undefined
})
type Schema = typeof state
function validate(state: Partial<Schema>): FormError[] {
const errors = []
if (!state.email) errors.push({ name: 'email', message: 'Required' })
if (!state.password) errors.push({ name: 'password', message: 'Required' })
return errors
}
const toast = useToast()
async function onSubmit(event: FormSubmitEvent<Schema>) {
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' })
console.log(event.data)
}
async function onError(event: FormErrorEvent) {
if (event?.errors?.[0]?.id) {
const element = document.getElementById(event.errors[0].id)
element?.focus()
element?.scrollIntoView({ behavior: 'smooth', block: 'center' })
}
}
</script>
<template>
<UForm :validate="validate" :state="state" class="space-y-4" @submit="onSubmit" @error="onError">
<UFormField label="Email" name="email">
<UInput v-model="state.email" />
</UFormField>
<UFormField label="Password" name="password">
<UInput v-model="state.password" type="password" />
</UFormField>
<UButton type="submit">
Submit
</UButton>
</UForm>
</template>
HTML5検証badge{label="4.5+" class="align-text-top"}
form.submit()をプログラムで呼び出すと、フォームコンポーネントは送信前にネイティブHTML 5検証を自動的にトリガーします。
<script setup lang="ts">
import type { FormSubmitEvent } from '@nuxt/ui'
const state = reactive({
email: undefined,
age: undefined
})
type Schema = typeof state
const form = useTemplateRef('form')
const toast = useToast()
async function onSubmit(event: FormSubmitEvent<Schema>) {
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' })
console.log(event.data)
}
</script>
<template>
<div class="space-y-4">
<UForm ref="form" :state="state" class="space-y-4" @submit="onSubmit">
<UFormField label="Email" name="email">
<UInput v-model="state.email" type="email" required />
</UFormField>
<UFormField label="Age" name="age">
<UInput v-model="state.age" type="number" min="18" max="100" required />
</UFormField>
</UForm>
<UButton @click="form?.submit()">
Submit
</UButton>
</div>
</template>
<script setup lang="ts">
import { reactive, useTemplateRef } from 'vue'
import type { FormSubmitEvent } from '@nuxt/ui'
const state = reactive({
email: undefined,
age: undefined
})
type Schema = typeof state
const form = useTemplateRef('form')
const toast = useToast()
async function onSubmit(event: FormSubmitEvent<Schema>) {
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' })
console.log(event.data)
}
</script>
<template>
<div class="space-y-4">
<UForm ref="form" :state="state" class="space-y-4" @submit="onSubmit">
<UFormField label="Email" name="email">
<UInput v-model="state.email" type="email" required />
</UFormField>
<UFormField label="Age" name="age">
<UInput v-model="state.age" type="number" min="18" max="100" required />
</UFormField>
</UForm>
<UButton @click="form?.submit()">
Submit
</UButton>
</div>
</template>
ネスティングフォーム
nestedプロパティを使用して、複数のフォームコンポーネントをネストし、それらのバリデーション関数をリンクします。この場合、親フォームをバリデーションすると、その中の他のすべてのフォームが自動的にバリデーションされます。
ネストされたフォームは親のstateを直接継承するので、別々のstateを定義する必要はありません。nameプロパティを使用して、親のstate内のネストされた属性をターゲットにできます。
ユーザーの入力に基づいて動的にフィールドを追加するために使用できます。
<script setup lang="ts">
import * as z from 'zod'
import type { FormSubmitEvent } from '@nuxt/ui'
const schema = z.object({
name: z.string().min(2),
news: z.boolean().default(false)
})
type Schema = z.output<typeof schema>
const nestedSchema = z.object({
email: z.email()
})
type NestedSchema = z.output<typeof nestedSchema>
const state = reactive<Partial<Schema & NestedSchema>>({ })
const toast = useToast()
async function onSubmit(event: FormSubmitEvent<Schema>) {
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' })
console.log(event.data)
}
</script>
<template>
<UForm
ref="form"
:state="state"
:schema="schema"
class="gap-4 flex flex-col w-60"
@submit="onSubmit"
>
<UFormField label="Name" name="name">
<UInput v-model="state.name" placeholder="John Lennon" />
</UFormField>
<div>
<UCheckbox v-model="state.news" name="news" label="Register to our newsletter" @update:model-value="state.email = undefined" />
</div>
<UForm v-if="state.news" :schema="nestedSchema" nested>
<UFormField label="Email" name="email">
<UInput v-model="state.email" placeholder="john@lennon.com" />
</UFormField>
</UForm>
<div>
<UButton type="submit">
Submit
</UButton>
</div>
</UForm>
</template>
<script setup lang="ts">
import { reactive } from 'vue'
import * as z from 'zod'
import type { FormSubmitEvent } from '@nuxt/ui'
const schema = z.object({
name: z.string().min(2),
news: z.boolean().default(false)
})
type Schema = z.output<typeof schema>
const nestedSchema = z.object({
email: z.email()
})
type NestedSchema = z.output<typeof nestedSchema>
const state = reactive<Partial<Schema & NestedSchema>>({ })
const toast = useToast()
async function onSubmit(event: FormSubmitEvent<Schema>) {
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' })
console.log(event.data)
}
</script>
<template>
<UForm
ref="form"
:state="state"
:schema="schema"
class="gap-4 flex flex-col w-60"
@submit="onSubmit"
>
<UFormField label="Name" name="name">
<UInput v-model="state.name" placeholder="John Lennon" />
</UFormField>
<div>
<UCheckbox v-model="state.news" name="news" label="Register to our newsletter" @update:model-value="state.email = undefined" />
</div>
<UForm v-if="state.news" :schema="nestedSchema" nested>
<UFormField label="Email" name="email">
<UInput v-model="state.email" placeholder="john@lennon.com" />
</UFormField>
</UForm>
<div>
<UButton type="submit">
Submit
</UButton>
</div>
</UForm>
</template>
リスト入力を検証するには:
<script setup lang="ts">
import * as z from 'zod'
import type { FormSubmitEvent } from '@nuxt/ui'
const schema = z.object({
customer: z.string().min(2)
})
type Schema = z.output<typeof schema>
const itemSchema = z.object({
description: z.string().min(1),
price: z.number().min(0)
})
type ItemSchema = z.output<typeof itemSchema>
const state = reactive<Partial<Schema & { items: Partial<ItemSchema>[] }>>({ })
function addItem() {
if (!state.items) {
state.items = []
}
state.items.push({})
}
function removeItem() {
if (state.items) {
state.items.pop()
}
}
const toast = useToast()
async function onSubmit(event: FormSubmitEvent<Schema>) {
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' })
console.log(event.data)
}
</script>
<template>
<UForm
:state="state"
:schema="schema"
class="gap-4 flex flex-col w-60"
@submit="onSubmit"
>
<UFormField label="Customer" name="customer">
<UInput v-model="state.customer" placeholder="Wonka Industries" />
</UFormField>
<UForm
v-for="item, count in state.items"
:key="count"
:name="`items.${count}`"
:schema="itemSchema"
class="flex gap-2"
nested
>
<UFormField :label="!count ? 'Description' : undefined" name="description">
<UInput v-model="item.description" />
</UFormField>
<UFormField :label="!count ? 'Price' : undefined" name="price" class="w-20">
<UInput v-model="item.price" type="number" />
</UFormField>
</UForm>
<div class="flex gap-2">
<UButton color="neutral" variant="subtle" size="sm" @click="addItem()">
Add Item
</UButton>
<UButton color="neutral" variant="ghost" size="sm" @click="removeItem()">
Remove Item
</UButton>
</div>
<div>
<UButton type="submit">
Submit
</UButton>
</div>
</UForm>
</template>
<script setup lang="ts">
import { reactive } from 'vue'
import * as z from 'zod'
import type { FormSubmitEvent } from '@nuxt/ui'
const schema = z.object({
customer: z.string().min(2)
})
type Schema = z.output<typeof schema>
const itemSchema = z.object({
description: z.string().min(1),
price: z.number().min(0)
})
type ItemSchema = z.output<typeof itemSchema>
const state = reactive<Partial<Schema & { items: Partial<ItemSchema>[] }>>({ })
function addItem() {
if (!state.items) {
state.items = []
}
state.items.push({})
}
function removeItem() {
if (state.items) {
state.items.pop()
}
}
const toast = useToast()
async function onSubmit(event: FormSubmitEvent<Schema>) {
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' })
console.log(event.data)
}
</script>
<template>
<UForm
:state="state"
:schema="schema"
class="gap-4 flex flex-col w-60"
@submit="onSubmit"
>
<UFormField label="Customer" name="customer">
<UInput v-model="state.customer" placeholder="Wonka Industries" />
</UFormField>
<UForm
v-for="item, count in state.items"
:key="count"
:name="`items.${count}`"
:schema="itemSchema"
class="flex gap-2"
nested
>
<UFormField :label="!count ? 'Description' : undefined" name="description">
<UInput v-model="item.description" />
</UFormField>
<UFormField :label="!count ? 'Price' : undefined" name="price" class="w-20">
<UInput v-model="item.price" type="number" />
</UFormField>
</UForm>
<div class="flex gap-2">
<UButton color="neutral" variant="subtle" size="sm" @click="addItem()">
Add Item
</UButton>
<UButton color="neutral" variant="ghost" size="sm" @click="removeItem()">
Remove Item
</UButton>
</div>
<div>
<UButton type="submit">
Submit
</UButton>
</div>
</UForm>
</template>
API
Props
| Prop | Default | Type |
|---|---|---|
id | string | number | |
schema | SSchema to validate the form state. Supports Standard Schema objects, Yup, Joi, and Superstructs. | |
state | N extends false ? Partial<InferInput<S>> : neverAn object representing the current state of the form. | |
validate | (state: Partial<InferInput<S>>): FormError<string>[] | Promise<FormError<string>[]>Custom validation function to validate the form state. | |
validateOn | `['blur', 'change', 'input']` | FormInputEvents[]The list of input events that trigger the form validation. |
disabled | boolean Disable all inputs inside the form. | |
name | stringThe | |
validateOnInputDelay | 300 | numberDelay in milliseconds before validating the form on input events. |
transform | true as T | TIf true, applies schema transformations on submit. |
nested | `false` | NIf true, this form will attach to its parent Form and validate at the same time. |
loadingAuto | true | boolean When |
acceptcharset | string | |
action | string | |
autocomplete | string | |
enctype | string | |
method | string | |
novalidate | false | true | "true" | "false" | |
target | string | |
ui | { base?: any; } |
スロット
| Slot | Type |
|---|---|
default | { errors: FormErrorWithId[]; loading: boolean; } |
Emits
| Event | Type |
|---|---|
submit | [event: FormSubmitEvent<FormData<S, T>>] |
error | [event: FormErrorEvent] |
Expose
useTemplateRefを使用して型付きコンポーネントインスタンスにアクセスできます。
<script setup lang="ts">
const form = useTemplateRef('form')
</script>
<template>
<UForm ref="form" />
</template>
これにより、以下にアクセスできます:
| 名前 | タイプ |
|---|---|
submit() | Promise<void> HTML5検証でフォーム送信をトリガーします。 |
validate(opts: { name?: keyof T | (keyof T)[], silent?: boolean, nested?: boolean, transform?: boolean }) | Promise<T> フォーム検証をトリガーします。 |
clear(path?: keyof T | RegExp) | void 特定のパスに関連付けられたフォームエラーをクリアします。パスが指定されていない場合、すべてのフォームエラーをクリアします。 |
getErrors(path?: keyof T | RegExp) | FormErrorWithId[] 特定のパスに関連するフォームエラーを取得します。パスが指定されていない場合、すべてのフォームエラーを返します。 |
setErrors(errors: FormError[], name?: keyof T | RegExp) | void 指定したパスのエラーを設定します。パスが指定されていない場合、すべてのエラーを上書きします。 |
errors | Ref<FormErrorWithId[]> A検証エラーを含む配列への参照。これを使用してエラー情報にアクセスしたり操作したりします。 |
disabled | Ref<boolean> |
dirty | Ref<boolean> true少なくとも1つのフォームフィールドがユーザによって更新された場合。 |
dirtyFields | ReadonlySet<DeepReadonly<keyof T>>ユーザが変更したフィールドを追跡します。 |
touchedFields | ReadonlySet<DeepReadonly<keyof T>>ユーザが操作したフィールドを追跡します。 |
blurredFields | ReadonlySet<DeepReadonly<keyof T>>ユーザーがぼかしたフィールドを追跡します。 |
Theme
export default defineAppConfig({
ui: {
form: {
base: ''
}
}
})import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'
export default defineConfig({
plugins: [
vue(),
ui({
ui: {
form: {
base: ''
}
}
})
]
})