自动表单

根据 Zod 模式自动生成表单。

什么是自动表单

自动表单是一个适用于内部和低优先级表单的即用型表单构建器,它使用现有的 zod 模式。 例如,如果您已经拥有用于 API 的 zod 模式,并且想要创建一个简单的管理面板来编辑用户资料,只需将模式传递给自动表单,就完成了。

安装

运行以下命令

bash
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。

描述

您可以使用 describe 方法为每个字段设置标签。 如果没有设置标签,则将使用字段名称并将其转换为驼峰式。

ts
const formSchema = z.object({
  username: z.string().describe('Your username'),
  someValue: z.string(), // Will be "Some Value"
})

您还可以使用 fieldConfig 配置标签。

可选字段

默认情况下,所有字段都是必填项。 您可以使用 optional 方法使字段变为可选。

ts
const formSchema = z.object({
  username: z.string().optional(),
})

默认值

您可以使用 default 方法为字段设置默认值。

ts
const formSchema = z.object({
  favouriteNumber: z.number().default(5),
})

如果要设置日期的默认值,请先使用 new Date(val) 将其转换为 Date。

子对象

您可以嵌套对象来创建手风琴部分。

ts
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 方法为该部分设置标签和描述。

ts
const formSchema = z.object({
  address: z
    .object({
      street: z.string(),
      city: z.string(),
      zip: z.string(),
    })
    .describe('Your address'),
})

选择器/枚举

自动表单支持 enumnativeEnum 来创建选择器字段。

ts
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),
})

数组

自动表单支持对象数组。 因为从字符串/数字等的数组中推断出字段标签等内容很困难,所以只支持对象。

ts
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() 设置数组的默认值,但请确保数组元素与模式具有相同的结构。

ts
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 添加其他配置。

vue
<template>
  <AutoForm
    :field-config="{
      username: {
        // fieldConfig
      },
    }"
  />
</template>

标签

如果您想覆盖通过 Zod 的描述 预定义的标签,可以使用 label 属性自定义标签。

vue
<template>
  <AutoForm
    :field-config="{
      username: {
        label: 'Custom username',
      },
    }"
  />
</template>

描述

您可以使用 description 属性在字段下方添加描述。

vue
<template>
  <AutoForm
    :field-config="{
      username: {
        description: 'Enter a unique username. This will be shown to other users.',
      },
    }"
  />
</template>

输入框属性

您可以使用 inputProps 属性将属性传递给输入框组件。 您可以使用 HTML 组件接受的任何属性。

vue
<template>
  <AutoForm
    :field-config="{
      username: {
        inputProps: {
          type: 'text',
          placeholder: 'Username',
        },
      },
    }"
  />
</template>

// This will be rendered as:
<input type="text" placeholder="Username" />

可以通过在 inputProps 中使用 showLabel 属性来禁用输入框的标签。

vue
<template>
  <AutoForm
    :field-config="{
      username: {
        inputProps: {
          type: 'text',
          placeholder: 'Username',
          showLabel: false,
        },
      },
    }"
  />
</template>

组件

默认情况下,自动表单将使用 Zod 类型来确定要使用的输入组件。 您可以使用 component 属性覆盖此行为。

vue
<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

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 中传递上面的组件。

vue
<template>
  <AutoForm
    :field-config="{
      username: {
        component: CustomField,
      },
    }"
  />
</template>

命名插槽

您可以使用 Vue 命名插槽来自定义呈现的 AutoFormField

vue
<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 事件。 当表单提交且数据有效时,将调用此事件。

vue
<template>
  <AutoForm
    @submit="(data) => {
      // Do something with the data
    }"
  />
</template>

受控表单

通过将 form 作为属性传递,您可以控制和使用 Form 提供的方法。

vue
<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"

vue
<template>
  <AutoForm>
    <CustomButton type="submit">
      Send now
    </CustomButton>
  </AutoForm>

  // or
  <AutoForm>
    <button type="submit">
      Send now
    </button>
  </AutoForm>
</template>

添加其他元素

传递给 AutoForm 组件的所有子节点都将在表单下方呈现。

vue
<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 组件。

vue
<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 方法根据其他字段的值来验证表单。

您可以为同一字段和依赖关系类型创建多个依赖关系 - 例如,根据多个其他字段隐藏字段。 然后,当任何依赖关系满足时,将隐藏该字段。

示例

基本

Your favourite number between 1 and 10.

I agree to the .

We need your birthday to send you a gift.

无标签输入

此示例展示如何在没有标签的情况下使用 AutoForm 输入。

子对象

根据 Zod schema 自动生成表单。

受控的

此示例展示如何在受控的情况下使用 AutoForm。

确认密码

经过细化的 schema 来验证两个字段是否匹配。

API 示例

表单选择项从 API 获取。

加载中…

数组支持

您可以在 schema 中使用数组来创建动态表单。

依赖项

创建字段之间的依赖关系。

Setting this below 18 will require parents consent.

Setting this to true will remove non-vegetarian food options.

在 GitHub 上编辑此页面