Form

GitHub
一个具有内置验证和提交处理功能的表单组件。

用法

使用表单组件,可以使用任何支持标准 Schema的验证库来验证表单数据,例如Valibot, Zod, Regle, Yup, JoiSuperstruct或您自己的验证逻辑。

它与 FormField 组件配合使用,可自动在表单元素周围显示错误消息。

Schema 验证

它需要两个 prop

默认情况下 不包含任何验证库,请确保您 安装所需的库
<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>

错误直接报告给 FormField 组件,基于 nameerror-pattern prop。这意味着您 schema 中为 email 属性定义的验证规则将应用于 <FormField name="email">

嵌套验证规则使用点符号处理。例如,像 { user: z.object({ email: z.string() }) } 这样的规则将应用于 <FormField name="user.email">

自定义验证

使用 validate prop 来应用您自己的验证逻辑。

验证函数必须返回一个包含以下属性的错误列表

  • message - 要显示的错误消息。
  • name - 要将错误发送到的 FormFieldname
它可以与 schema prop 一起使用,以处理复杂的用例。
<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>

输入事件

当输入发出 inputchangeblur 事件时,Form 组件会自动触发验证。

  • input 上验证发生在 您键入时
  • change 上验证发生在您 提交值时
  • blur 上验证发生在输入 失去焦点时

您可以使用 validate-on prop 来控制何时进行验证。

表单总是在提交时进行验证。
单选组
复选框组
在此处拖放您的图像
PNG(最大 1MB)
您可以使用 useFormField 可组合函数在您自己的组件中实现此功能。

错误事件

您可以监听 @error 事件来处理错误。此事件在表单提交时触发,并包含一个 FormError 对象数组,具有以下字段

  • id - 输入的 id
  • name - FormFieldname
  • 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 prop 来嵌套多个 Form 组件并链接它们的验证函数。在这种情况下,验证父表单将自动验证其内部的所有其他表单。

嵌套表单直接继承其父级的状态,因此您无需为它们定义单独的状态。您可以使用 name prop 来定位父级状态中的嵌套属性。

它可用于根据用户的输入动态添加字段

<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

string | number

schema

Struct<any, any> | StandardSchemaV1<object, object>

用于验证表单状态的 Schema。支持标准 Schema 对象、Yup、Joi 和 Superstructs。

state

Partial<any>

一个表示表单当前状态的对象。

validate

(state: Partial<any>): FormError<string>[] | Promise<FormError<string>[]>

用于验证表单状态的自定义验证函数。

验证时机

['blur', 'change', 'input']

FormInputEvents[]

触发表单验证的输入事件列表。

disabled

boolean

禁用表单内的所有输入。

name

string

表单在其父表单中的状态路径。用于嵌套表单。仅当 nested 为 true 时可用。

validateOnInputDelay

300

number

输入事件触发表单验证前的延迟毫秒数。

transform

true as T

boolean

如果为 true,则在提交时应用 Schema 转换。

nested

false

boolean

如果为 true,此表单将附加到其父表单并同时进行验证。

loadingAuto

true

boolean

true 时,所有表单元素将在 @submit 事件时禁用。这将导致任何获得焦点的输入元素失去其焦点状态。

插槽

插槽类型
default

{ errors: FormError<string>[]; loading: boolean; }

事件

事件类型
submit

[event: FormSubmitEvent<any>]

error

[event: FormErrorEvent]

可访问属性

您可以使用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>

触发表单验证。除非 opts.silent 设置为 true,否则将引发任何错误。

clear(path?: keyof T | RegExp)void

清除与特定路径关联的表单错误。如果未提供路径,则清除所有表单错误。

getErrors(path?: keyof T RegExp)FormError[]

检索与特定路径关联的表单错误。如果未提供路径,则返回所有表单错误。

setErrors(errors: FormError[], name?: keyof T RegExp)void

为给定路径设置表单错误。如果未提供路径,则覆盖所有错误。

errorsRef<FormError[]>

对包含验证错误的数组的引用。使用此项可以访问或操作错误信息。

disabledRef<boolean>
dirtyRef<boolean> 如果至少一个表单字段已由用户更新,则为 true
dirtyFieldsDeepReadonly<Set<keyof T>> 跟踪已被用户修改的字段。
touchedFieldsDeepReadonly<Set<keyof T>> 跟踪用户与之交互的字段。
blurredFieldsDeepReadonly<Set<keyof T>> 跟踪用户模糊的字段。

主题

app.config.ts
export default defineAppConfig({
  ui: {
    form: {
      base: ''
    }
  }
})
vite.config.ts
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: ''
        }
      }
    })
  ]
})

更新日志

817b9— 修复:不稳定的响应性测试 (#5033)

77a55— 修复:改进嵌套表单验证处理 (#5024)

99dbe— 修复!:如果启用了转换,不要改变表单的状态 (#4902)

2269b— 修复:处理 clear 函数上的竞态条件 (#4843)

5cb65— 特性:导入 @nuxt/ui-pro 组件

7133f— 修复:update:model-value 事件类型错误 (#4853)

ec2bc— 特性:导出表单错误注入键 (#4808)

a32cc— 修复:默认插槽类型 (#4758)

b8b74— 特性:在暴露的方法中支持错误正则表达式 (#4608)

5ea35— 文档:添加仅提交验证的提示 (#4554)

6f2ce— 重构:统一 emits 声明的语法 (#4512)

1a8fe— 修复:暴露响应式字段 (#4386)

ea0c4— 特性:将加载状态暴露给默认插槽 (#4247)

37abc— 修复:通过 transform prop 有条件地推断表单数据类型 (#4188)

f4294— 修复:输入和输出类型推断 (#3938)

1a0d7— 特性:添加 attach prop 以选择退出嵌套表单附件 (#3939)

8e78e— 修复:提交时失去焦点 (#3796)

fdee2— 特性:导出加载状态 (#3861)

39c86— fix:@nuxt/module-builder 升级后重构类型(#3855)

3dd88— 修复:提交后清除脏状态 (#3692)

02184— 杂项:改进 TSDoc (#3619)