表单配置化生成方案
一个简单的基于 vue3 的表单配置化生成方案,可以作为思路参考。
前言
本方案是个人在做某个项目时,需要实现一个简单的表单配置化生成,而做的一个方案。 最终结果是通过一份表单配置,渲染为一个表单,并支持:
- 支持多种表单字段类型 (单行文本/多行文本/数字/下拉/多选/单选 等),并支持嵌套字段(对象/数组)
- 支持字段校验
- 支持条件判断显示/隐藏 表单字段
- 支持字段分组展示
提示
完整代码仓库地址: formative
效果展示
通过配置生成表单:
配置的字段为对象或者数组时:
对配置的字段进行分组展示:
方案说明
通过配置生成表单,需要明确如何实现 表单字段 与对应的表单组件进行关联,并实现数据绑定。
协议配置
约定一个协议,通过一个对象来表示 表单中某一个字段:
FieldSchema:
{
"type": "表示字段的类型,比如单选、多选、单行文本、多行文本等",
"field": "表示字段的名",
"label": "表示字段显示的标签",
"default": "字段默认值",
"description": "字段描述信息,作为提示信息使用"
}
对于完整的表单,则使用 FieldSchema[]
的数组形式进行配置。
表单字段与组件关联
在 vue 中, 组件可以通过 h(component, attrs[, children])
渲染函数 进行渲染, 通过 h()
函数,可以实现动态渲染不同的组件。
我们可以利用这一特性,首先 字段类型与对应的组件进行映射,通过映射关系,用 字段类型获取对应的组件, 然后调用 h()
进行渲染。
// 字段类型 与 组件 映射
const componentMap = {
radio: RadioField,
checkbox: CheckboxField,
text: TextField,
select: SelectField,
}
{
name: 'Field',
props: ['fieldType'],
setup(props) {
return () => h(componentMap[props.fieldType])
}
}
表单数据绑定
对于表单数据,需要实现 数据初始化,以及 对应的 Field组件对数据的可读写。
在vue中,我们可以使用 provide/inject
API, 在 表单组件的根组件,通过 provide()
进行注入,在内部的 FieldComponent
中通过 inject()
获取对应的数据,进行读写。
/**
* 通过 provide 输入数据
*/
const useFormDataProvide = (formData: FormData): FormInjectKey => {
const key: FormInjectKey = Symbol('formData')
provide(key, formData)
return key
}
/**
* 通过 inject 获取数据
*/
const useFormData = (key: FormInjectKey): FormData => inject<FormData>(key)!
由于需要支持 字段为对象/数组时产生的 多层级数据结构,还需要考虑 FieldComponent
对 FormData.a.b
的 深层数据的读写。
可以通过实现 dotPath
, 通过 FormData['a.b']
的形式来读写 FormData.a.b
,这样 FieldComponent
只需要知道 a.b
的 dotPath
字段路径即可配合 inject()
获取的表单数据进行读写。
const useDotPath = <T = any>(model: FormData, dotKey: ComputedRef<string>) => {
const binding = computed<T>({
set(data) {
setDotPath(model.value, dotKey.value, data)
},
get() {
return getDotPath(model.value, dotKey.value)
},
})
return binding
}
技术实现
在 formative 仓库中, 通过本方案实现了一个 配置化生成表单的库。
src/components/Formative.tsx
文件作为表单根组件。src/components/Field.tsx
文件作为根据表单字段动态选择对应组件进行表单字段渲染。src/components/Group.tsx
文件实现了对表单字段进行分组。src/components/[other].tsx
其他文件实现了 各种表单字段组件。
有兴趣了解细节的,可以阅读 formative 源码, 我在关键位置进行了相关的代码注释说明。
同时,你可以直接拉取 源码,在本地进行运行。并授权代码使用。