9.7k

表单

上一页下一页

使用 VeeValidate 和 Zod 构建表单。

表单处理起来很棘手。它们是你构建 Web 应用时最常见的功能之一,但同时也是最复杂的。

设计良好的 HTML 表单应该:

  • 结构清晰且语义正确。
  • 易于使用和导航(键盘支持)。
  • 具有无障碍性,包含 ARIA 属性和正确的标签。
  • 支持客户端和服务器端验证。
  • 样式美观并与应用的其余部分保持一致。

在本指南中,我们将探讨如何使用 vee-validatezod 构建表单。我们将使用 <FormField> 组件,结合 Reka UI 组件来编写无障碍表单。

特性

<Form /> 组件是对 vee-validate 库的封装。它提供了以下功能:

  • 用于构建表单的可组合组件。
  • 用于构建受控表单字段的 <FormField /> 组件。
  • 使用 zod 进行表单验证。
  • 根据状态自动应用正确的 aria 属性,并处理唯一 ID。
  • 专为与所有 Reka UI 组件协作而设计。
  • 支持自定义架构验证库。我们默认使用 zod,但你也可以使用任何其他支持的库,如 yupvalibot
  • 你拥有对标记和样式的完全控制权。

vee-validate 使用两种方式为你的表单添加验证:

  • 组合式 API (Composition API)
  • 高阶组件 (HOC)

结构说明

<template>
  <Form>
    <FormField>
      <FormItem>
        <FormLabel />
        <FormControl>
        <!-- any Form Input component or native input elements -->
        </FormControl>
        <FormDescription />
        <FormMessage />
      </FormItem>
    </FormField>
  </Form>
</template>

示例

Input 组件

<template>
  <FormField v-slot="{ componentField }">
    <FormItem>
      <FormLabel>Username</FormLabel>
      <FormControl>
        <Input placeholder="shadcn" v-bind="componentField" />
      </FormControl>
      <FormDescription />
      <FormMessage />
    </FormItem>
  </FormField>
</template>

安装

pnpm dlx shadcn-vue@latest add form

使用方法

<script setup lang="ts">
import {
  FormControl,
  FormDescription,
  FormField,
  FormItem,
  FormLabel,
  FormMessage,
} from '@/components/ui/form'
</script>

<template>
  <FormField v-slot="{ componentField }" name="username">
    <FormItem>
      <FormLabel>Username</FormLabel>
      <FormControl>
        <Input placeholder="shadcn" v-bind="componentField" />
      </FormControl>
      <FormDescription>
        This is your public display name.
      </FormDescription>
      <FormMessage />
    </FormItem>
  </FormField>
</template>

创建表单架构

使用 Zod 架构定义表单形状。你可以在 Zod 文档 中了解更多关于如何使用 Zod 的信息。

使用 @vee-validate/zod 将 Zod 架构验证与 vee-validate 集成。

toTypedSchema 还可以使表单值和提交后的值自动获得类型推断,同时满足架构的输入和输出类型需求。

<script setup lang="ts">
import { toTypedSchema } from '@vee-validate/zod'
import * as z from 'zod'

const formSchema = toTypedSchema(z.object({
  username: z.string().min(2).max(50),
}))
</script>

定义表单

使用 vee-validate 提供的 useForm 组合函数,或者使用 <Form /> 组件来创建表单。

<script setup lang="ts">
import { useForm } from 'vee-validate'
import { toTypedSchema } from '@vee-validate/zod'
import * as z from 'zod'

import {
  FormControl,
  FormDescription,
  FormField,
  FormItem,
  FormLabel,
  FormMessage
} from '@/components/ui/form'

const formSchema = toTypedSchema(z.object({
  username: z.string().min(2).max(50),
}))

const form = useForm({
  validationSchema: formSchema,
})

const onSubmit = form.handleSubmit((values) => {
  console.log('Form submitted!', values)
})
</script>

<template>
  <form @submit="onSubmit">
    ...
  </form>
</template>

构建你的表单

基于上一步,我们可以使用 <Form /> 组件或 useForm 组合函数。推荐使用 useForm,因为值会自动获得类型推断。

<script setup lang="ts">
import { toTypedSchema } from '@vee-validate/zod'
import { useForm } from 'vee-validate'
import * as z from 'zod'

import { Button } from '@/components/ui/button'
import {
  FormControl,
  FormDescription,
  FormField,
  FormItem,
  FormLabel,
  FormMessage,
} from '@/components/ui/form'
import { Input } from '@/components/ui/input'

const formSchema = toTypedSchema(z.object({
  username: z.string().min(2).max(50),
}))

const form = useForm({
  validationSchema: formSchema,
})

const onSubmit = form.handleSubmit((values) => {
  console.log('Form submitted!', values)
})
</script>

<template>
  <form @submit="onSubmit">
    <FormField v-slot="{ componentField }" name="username">
      <FormItem>
        <FormLabel>Username</FormLabel>
        <FormControl>
          <Input type="text" placeholder="shadcn" v-bind="componentField" />
        </FormControl>
        <FormDescription>
          This is your public display name.
        </FormDescription>
        <FormMessage />
      </FormItem>
    </FormField>
    <Button type="submit">
      Submit
    </Button>
  </form>
</template>

完成

就是这样。你现在拥有了一个完全可访问、类型安全且具备客户端验证功能的表单。

这是你的公开显示名称。

<script setup lang="ts">
import { toTypedSchema } from '@vee-validate/zod'
import { useForm } from 'vee-validate'
import { z } from 'zod'

import { Button } from '@/components/ui/button'
import {
  FormControl,
  FormDescription,
  FormField,
  FormItem,
  FormLabel,
  FormMessage,
} from '@/components/ui/form'
import { Input } from '@/components/ui/input'

const formSchema = toTypedSchema(z.object({
  username: z.string().min(2, {
    message: 'Username must be at least 2 characters.',
  }),
}))

const form = useForm({
  validationSchema: formSchema,
})

const onSubmit = form.handleSubmit((values) => {
  console.log('Form submitted!', values)
})
</script>

<template>
  <form class="w-2/3 space-y-6" @submit="onSubmit">
    <FormField v-slot="{ componentField }" name="username">
      <FormItem>
        <FormLabel>Username</FormLabel>
        <FormControl>
          <Input type="text" placeholder="shadcn" v-bind="componentField" />
        </FormControl>
        <FormDescription>
          This is your public display name.
        </FormDescription>
        <FormMessage />
      </FormItem>
    </FormField>
    <Button type="submit">
      Submit
    </Button>
  </form>
</template>