自动表单
根据 Zod 模式自动生成表单。
致谢: 受到 Vantezzen 的 自动表单 的启发。
什么是自动表单
自动表单是一个适用于内部和低优先级表单的即用型表单构建器,它使用现有的 zod 模式。 例如,如果您已经拥有用于 API 的 zod 模式,并且想要创建一个简单的管理面板来编辑用户资料,只需将模式传递给自动表单,就完成了。
安装
运行以下命令
npx shadcn-vue@latest update form
npx shadcn-vue@latest add auto-form
字段类型
目前,这些字段类型开箱即用地得到支持。
- boolean(复选框,开关)
- date(日期选择器)
- enum(选择器,单选按钮组)
- number(输入框)
- string(输入框,文本字段)
- file(文件)
您可以通过将它们添加到 auto-form/constants.ts
中的 INPUT_COMPONENTS
对象来添加对其他字段类型的支持。
Zod 配置
验证
您的表单模式可以使用任何 zod 的验证方法,包括 refine。
⚠️ 但是,Zod 的 refine
和 superRefine
存在已知问题,当某些对象键丢失时,它们不会执行。 了解更多
描述
您可以使用 describe
方法为每个字段设置标签。 如果没有设置标签,则将使用字段名称并将其转换为驼峰式。
const formSchema = z.object({
username: z.string().describe('Your username'),
someValue: z.string(), // Will be "Some Value"
})
您还可以使用 fieldConfig
配置标签。
可选字段
默认情况下,所有字段都是必填项。 您可以使用 optional
方法使字段变为可选。
const formSchema = z.object({
username: z.string().optional(),
})
默认值
您可以使用 default
方法为字段设置默认值。
const formSchema = z.object({
favouriteNumber: z.number().default(5),
})
如果要设置日期的默认值,请先使用 new Date(val)
将其转换为 Date。
子对象
您可以嵌套对象来创建手风琴部分。
const formSchema = z.object({
address: z.object({
street: z.string(),
city: z.string(),
zip: z.string(),
// You can nest objects as deep as you want
nested: z.object({
foo: z.string(),
bar: z.string(),
nested: z.object({
foo: z.string(),
bar: z.string(),
}),
}),
}),
})
与普通对象一样,您可以使用 describe
方法为该部分设置标签和描述。
const formSchema = z.object({
address: z
.object({
street: z.string(),
city: z.string(),
zip: z.string(),
})
.describe('Your address'),
})
选择器/枚举
自动表单支持 enum
和 nativeEnum
来创建选择器字段。
const formSchema = z.object({
color: z.enum(['red', 'green', 'blue']),
})
enum BreadTypes {
// For native enums, you can alternatively define a backed enum to set a custom label
White = 'White bread',
Brown = 'Brown bread',
Wholegrain = 'Wholegrain bread',
Other,
}
// Keep in mind that zod will validate and return the enum labels, not the enum values!
const formSchema = z.object({
bread: z.nativeEnum(BreadTypes),
})
数组
自动表单支持对象数组。 因为从字符串/数字等的数组中推断出字段标签等内容很困难,所以只支持对象。
const formSchema = z.object({
guestListName: z.string(),
invitedGuests: z
.array(
// Define the fields for each item
z.object({
name: z.string(),
age: z.number(),
})
)
// Optionally set a custom label - otherwise this will be inferred from the field name
.describe('Guests invited to the party'),
})
数组不被支持作为表单模式的根元素。
您还可以使用 .default() 设置数组的默认值,但请确保数组元素与模式具有相同的结构。
const formSchema = z.object({
guestListName: z.string(),
invitedGuests: z
.array(
// Define the fields for each item
z.object({
name: z.string(),
age: z.number(),
})
)
.describe('Guests invited to the party')
.default([
{ name: 'John', age: 24, },
{ name: 'Jane', age: 20, },
]),
})
字段配置
由于 zod 不允许在模式中添加其他属性,因此您可以使用 fieldConfig
属性为每个字段的 UI 添加其他配置。
<template>
<AutoForm
:field-config="{
username: {
// fieldConfig
},
}"
/>
</template>
标签
如果您想覆盖通过 Zod 的描述 预定义的标签,可以使用 label
属性自定义标签。
<template>
<AutoForm
:field-config="{
username: {
label: 'Custom username',
},
}"
/>
</template>
描述
您可以使用 description
属性在字段下方添加描述。
<template>
<AutoForm
:field-config="{
username: {
description: 'Enter a unique username. This will be shown to other users.',
},
}"
/>
</template>
输入框属性
您可以使用 inputProps
属性将属性传递给输入框组件。 您可以使用 HTML 组件接受的任何属性。
<template>
<AutoForm
:field-config="{
username: {
inputProps: {
type: 'text',
placeholder: 'Username',
},
},
}"
/>
</template>
// This will be rendered as:
<input type="text" placeholder="Username" />
可以通过在 inputProps
中使用 showLabel
属性来禁用输入框的标签。
<template>
<AutoForm
:field-config="{
username: {
inputProps: {
type: 'text',
placeholder: 'Username',
showLabel: false,
},
},
}"
/>
</template>
组件
默认情况下,自动表单将使用 Zod 类型来确定要使用的输入组件。 您可以使用 component
属性覆盖此行为。
<template>
<AutoForm
:field-config="{
acceptTerms: {
// Booleans use a checkbox by default, use a switch instead
component: 'switch',
},
}"
/>
</template>
支持的字段类型的完整列表是类型化的。 当前支持的类型有
checkbox
(布尔值的默认值)switch
date
(日期的默认值)select
(枚举的默认值)radio
textarea
或者,您可以将 Vue 组件传递给 component
属性以使用自定义组件。
在 CustomField.vue
中
<script setup lang="ts">
import { computed } from 'vue'
import AutoFormLabel from './AutoFormLabel.vue'
import type { FieldProps } from './interface'
import { FormControl, FormDescription, FormField, FormItem, FormMessage } from '@/ui/form'
import { Input } from '@/ui/input'
import { AutoFormLabel } from '@/ui/auto-form'
const props = defineProps<FieldProps>()
</script>
<template>
<FormField v-slot="slotProps" :name="fieldName">
<FormItem v-bind="$attrs">
<AutoFormLabel v-if="!config?.hideLabel" :required="required">
{{ config?.label }}
</AutoFormLabel>
<FormControl>
<CustomInput v-bind="slotProps" />
</FormControl>
<FormDescription v-if="config?.description">
{{ config.description }}
</FormDescription>
<FormMessage />
</FormItem>
</FormField>
</template>
在 fieldConfig
中传递上面的组件。
<template>
<AutoForm
:field-config="{
username: {
component: CustomField,
},
}"
/>
</template>
命名插槽
您可以使用 Vue 命名插槽来自定义呈现的 AutoFormField
。
<template>
<AutoForm
:field-config="{
customParent: {
label: 'Wrapper',
},
}"
>
<template #customParent="slotProps">
<div class="flex items-end space-x-2">
<AutoFormField v-bind="slotProps" class="w-full" />
<Button type="button">
Check
</Button>
</div>
</template>
</AutoForm>
</template>
访问表单数据
有两种方法可以访问表单数据。
@submit
首选方法是使用 submit
事件。 当表单提交且数据有效时,将调用此事件。
<template>
<AutoForm
@submit="(data) => {
// Do something with the data
}"
/>
</template>
受控表单
通过将 form
作为属性传递,您可以控制和使用 Form
提供的方法。
<script setup lang="ts">
import * as z from 'zod'
import { useForm } from 'vee-validate'
import { toTypedSchema } from '@vee-validate/zod'
const schema = z.object({
username: z.string(),
})
const form = useForm({
validationSchema: toTypedSchema(schema),
})
form.setFieldValue('username', 'bar')
</script>
<template>
<AutoForm :form="form" :schema="schema" />
</template>
提交表单
您可以使用任何 button
组件来创建提交按钮。 最重要的是添加属性 type="submit"
。
<template>
<AutoForm>
<CustomButton type="submit">
Send now
</CustomButton>
</AutoForm>
// or
<AutoForm>
<button type="submit">
Send now
</button>
</AutoForm>
</template>
添加其他元素
传递给 AutoForm
组件的所有子节点都将在表单下方呈现。
<template>
<AutoForm>
<Button>Send now</Button>
<p class="text-gray-500 text-sm">
By submitting this form, you agree to our
<a href="#" class="text-primary underline">
terms and conditions
</a>.
</p>
</AutoForm>
</template>
依赖关系
自动表单允许您在字段之间添加依赖关系,以便根据其他字段的值来控制字段。 为此,可以将 dependencies
数组传递给 AutoForm
组件。
<template>
<AutoForm
:dependencies="[
{
// 'age' hides 'parentsAllowed' when the age is 18 or older
sourceField: 'age',
type: DependencyType.HIDES,
targetField: 'parentsAllowed',
when: age => age >= 18,
},
{
// 'vegetarian' checkbox hides the 'Beef Wellington' option from 'mealOptions'
// if its not already selected
sourceField: 'vegetarian',
type: DependencyType.SETS_OPTIONS,
targetField: 'mealOptions',
when: (vegetarian, mealOption) =>
vegetarian && mealOption !== 'Beef Wellington',
options: ['Pasta', 'Salad'],
},
]"
/>
</template>
支持以下依赖关系类型
DependencyType.HIDES
:当when
函数返回 true 时,隐藏目标字段DependencyType.DISABLES
:当when
函数返回 true 时,禁用目标字段DependencyType.REQUIRES
:当when
函数返回 true 时,将目标字段设置为必填项DependencyType.SETS_OPTIONS
:当when
函数返回 true 时,将目标字段的选项设置为options
数组
when
函数使用源字段的值和目标字段的值进行调用,并应返回一个布尔值,以指示是否应应用依赖关系。
请注意,当返回 false
时,依赖关系不会导致反向操作 - 例如,如果您在 zod 模式中将字段标记为必填项(即,没有明确设置 optional
),则在 REQURIES
依赖关系中返回 false
不会将其标记为可选。 您应该改为使用 zod 的 optional
方法将其默认标记为可选,并使用 REQURIES
依赖关系将其在满足依赖关系时标记为必填项。
请注意,依赖关系不会对表单的验证产生任何影响。 您应该使用 zod 的 refine
方法根据其他字段的值来验证表单。
您可以为同一字段和依赖关系类型创建多个依赖关系 - 例如,根据多个其他字段隐藏字段。 然后,当任何依赖关系满足时,将隐藏该字段。
示例
基本
无标签输入
此示例展示如何在没有标签的情况下使用 AutoForm 输入。
子对象
根据 Zod schema 自动生成表单。
受控的
此示例展示如何在受控的情况下使用 AutoForm。
确认密码
经过细化的 schema 来验证两个字段是否匹配。
API 示例
表单选择项从 API 获取。
数组支持
您可以在 schema 中使用数组来创建动态表单。
依赖项
创建字段之间的依赖关系。