Form

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

用法

使用 Form 组件通过任何支持以下条件的验证库来验证表单数据:标准模式例如Valibot, Zod, Regle, Yup, JoiSuperstruct或您自己的验证逻辑。

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

模式验证

它需要两个属性:

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

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

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

自定义验证

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

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

  • message - 要显示的错误消息。
  • name - 要将错误发送到的 FormFieldname
它可以与 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>

输入事件

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

  • input 上的验证会在您输入时发生。
  • change 上的验证会在您确认值时发生。
  • blur 上的验证会在输入框失去焦点时发生。

您可以使用 validate-on 属性来控制何时发生验证。

表单始终在提交时进行验证。
单选组
复选框组
在此处拖放您的图片
PNG(最大 1MB)
您可以使用 useFormField Composable 来在您自己的组件中实现此功能。

错误事件

您可以监听 @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 属性来嵌套多个 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

string | number

schema

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

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

state

Partial<any>

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

validate

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

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

validateOn

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

FormInputEvents[]

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

disabled

boolean

禁用表单内的所有输入。

name

string

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

validateOnInputDelay

300

number

在输入事件上验证表单之前的延迟(以毫秒为单位)。

transform

true as T

boolean

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

nested

false

boolean

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

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— fix: flaky reactivity tests (#5033)

77a55— fix: improve nested form validation handling (#5024)

99dbe— fix!: don't mutate the form's state if transformations are enabled (#4902)

2269b— fix: handling race condition on clear function (#4843)

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

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

ec2bc— feat: export form errors injection key (#4808)

a32cc— fix: default slot types (#4758)

b8b74— feat: support error RegExp in exposed methods (#4608)

5ea35— docs: add tips for submit only validation (#4554)

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

1a8fe— fix: expose reactive fields (#4386)

ea0c4— feat: expose loading state to default slot (#4247)

37abc— fix: conditionally type form data via transform prop (#4188)

f4294— fix: input and output type inference (#3938)

1a0d7— feat: add attach prop to opt-out of nested form attachement (#3939)

8e78e— fix: loses focus on submit (#3796)

fdee2— feat: export loading state (#3861)

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

3dd88— fix: clear dirty state after submit (#3692)

02184— chore: improve TSDoc (#3619)