Form
用法
使用 Form 组件通过任何支持以下条件的验证库来验证表单数据:标准模式例如Valibot, Zod, Regle, Yup, Joi或Superstruct或您自己的验证逻辑。
它与 FormField 组件配合使用,可自动围绕表单元素显示错误消息。
模式验证
它需要两个属性:
state
- 一个保存表单状态的响应式对象。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 * 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 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>
错误会根据 name
或 error-pattern
属性直接报告给 FormField 组件。这意味着为您模式中的 email
属性定义的验证规则将应用于 <FormField name="email">
。
使用点表示法处理嵌套的验证规则。例如,像 { user: z.object({ email: z.string() }) }
这样的规则将应用于 <FormField name="user.email">
。
自定义验证
使用 validate
属性来应用您自己的验证逻辑。
验证函数必须返回一个包含以下属性的错误列表:
message
- 要显示的错误消息。name
- 要将错误发送到的FormField
的name
。
schema
属性一起使用,以处理复杂用例。<script setup lang="ts">
import type { FormError, FormSubmitEvent } from '@nuxt/ui'
const state = reactive({
email: undefined,
password: undefined
})
const validate = (state: any): 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<typeof state>) {
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>
输入事件
当输入框发出 input
、change
或 blur
事件时,Form 组件会自动触发验证。
- 在
input
上的验证会在您输入时发生。 - 在
change
上的验证会在您确认值时发生。 - 在
blur
上的验证会在输入框失去焦点时发生。
您可以使用 validate-on
属性来控制何时发生验证。
useFormField
Composable 来在您自己的组件中实现此功能。错误事件
您可以监听 @error
事件来处理错误。当表单提交时会触发此事件,它包含一个 FormError
对象数组,其中包含以下字段:
id
- 输入框的id
。name
-FormField
的name
。message
- 要显示的错误消息。
下面是一个在表单提交后将焦点定位到第一个有错误的输入元素的示例。
<script setup lang="ts">
import type { FormError, FormErrorEvent, FormSubmitEvent } from '@nuxt/ui'
const state = reactive({
email: undefined,
password: undefined
})
const validate = (state: any): 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<typeof state>) {
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>
嵌套表单
使用 nested
属性来嵌套多个 Form 组件并链接它们的验证函数。在这种情况下,验证父表单将自动验证其中所有其他表单。
嵌套表单直接继承其父表单的状态,因此您无需为其定义单独的状态。您可以使用 name
属性来定位父表单状态中的嵌套属性。
这可以用于根据用户输入动态添加字段。
<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 * 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
属性
属性 | 默认值 | 类型 |
---|---|---|
id |
| |
schema |
用于验证表单状态的模式。支持标准模式对象、Yup、Joi 和 Superstruct。 | |
state |
一个表示表单当前状态的对象。 | |
validate |
自定义验证函数,用于验证表单状态。 | |
validateOn |
|
触发表单验证的输入事件列表。 |
disabled |
禁用表单内的所有输入。 | |
name |
表单状态在其父表单内的路径。用于嵌套表单。仅当 | |
validateOnInputDelay |
|
在输入事件上验证表单之前的延迟(以毫秒为单位)。 |
transform |
|
如果为 true,则在提交时应用模式转换。 |
nested |
|
如果为 true,此表单将附加到其父 Form 并同时进行验证。 |
loadingAuto |
|
当 |
插槽
插槽 | 类型 |
---|---|
default |
|
事件
事件 | 类型 |
---|---|
submit |
|
error |
|
可访问属性
您可以使用useTemplateRef
.
<script setup lang="ts">
const form = useTemplateRef('form')
</script>
<template>
<UForm ref="form" />
</template>
这将使您能够访问以下内容
名称 | 类型 |
---|---|
submit() | Promise<void> 触发表单提交。 |
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) | FormError[] 检索与特定路径关联的表单错误。如果未提供路径,则返回所有表单错误。 |
setErrors(errors: FormError[], name?: keyof T RegExp) | void 为给定的路径设置表单错误。如果未提供路径,则覆盖所有错误。 |
errors | Ref<FormError[]> 一个包含验证错误的数组的引用。使用此数组来访问或操作错误信息。 |
disabled | Ref<boolean> |
dirty | Ref<boolean> 如果用户至少更新了一个表单字段,则为 true 。 |
dirtyFields | DeepReadonly<Set<keyof T>> 跟踪用户修改过的字段。 |
touchedFields | DeepReadonly<Set<keyof T>> 跟踪用户与之交互过的字段。 |
blurredFields | DeepReadonly<Set<keyof T>> 跟踪用户失焦的字段。 |
主题
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: ''
}
}
})
]
})
更新日志
5cb65
— 特性:导入 @nuxt/ui-pro
组件