最近又重构了一次表单组件(react&&hook),力求使表单组件足够好用,并实现以下几个目标
看演示
- 配置化: 模板less,配置化实现表单
- 结构灵活: 除了表单本体之外,能够灵活的增加各种辅助结构
- 模块化: 表单不仅仅能作为form的一个部分,也能够拆分出来独立使用
- 动态化: 对表单可动态增减
- 联动: 表单之间的联动可通过配置化实现
- 可扩展: 表单需求千千万,当然必须要扩展
配置化
做模板less的好处是可以在JS就可以对逻辑,业务、渲染进行修改,在表单业务复杂的情况下,模板反而增加了很多不必要的工作量,最终希望模板仅仅只是一个标签引用
配置
const config = {
data: [
{input...},
{button onClick=responseClick...}
]
}
function responseClick(){
form.append({input...})
}
模板
<Form data={config} />
结构
单表单
一个完整的表单,除了表单本体,还有如出错信息,是否必选,表单标题等等属性需要一些辅助结构来帮助表达,如下图
上图只是一个简单的示意,应该还需要诸如password的小眼睛,下拉菜单的小箭头等等UI层面的辅助结构。诸如上图的这些辅助结构可以通过配置传入,有值时显示,无值时不显示,另外还需要配套api,按照需求调用它们
配置code
const input = {type: 'text', id: 'uniq-id', value: '你好', className: '...'}
复合表单
什么是复合表单,类似于省市区这样的表单就是,它由多个表单个体组合而成,并且表单需要支持各种类型, 如下图
配置code
const input = [
{title: '用户名', type: 'text', id: 'uniq-id', value: '...', className: '...'},
{title: '说明', type: 'text', id: 'uniq-id', value: '...', className: '...'}
]
组表单
多个复合表单或者单表单混合在一起构成,它是完整的表单的一个独立部分,多个组表单合并构成完整表单,如下图
配置code
const input = {
title: '组表单',
input: [
{表单-1},
{表单-2},
{表单-3},
]
}
完整表单
由多个组表单构成完整表单,如下图
配置code
data: [
// 表单组-1
{
title: '表单组1-标题',
input: [
表单-1,
表单-2
...
]
},
// 表单组-2
{
title: '表单组2-标题',
input: [
表单-1,
表单-2
...
]
},
...
]
模块化
在有了各种结构的表单零件之后,通过配置我们可以组合或者单独使用这些零件(在不同的场景中)
单独使用
const config = {title: '单个表单', value: '...'}
const Text = <form.Text data={config} />
组合使用
const config = [
{
title: '姓名',
type: 'text',
value: '...'
},
{
title: '职业',
type: 'text',
value: '...'
},
]
const group = <form.Group data={config} />
动态化
配置是死的,怎么配置就输出什么样的结构,但并不能满足产品需求。很多场景需要动态增减表单,并且还要保持原有填写的数据。以组表单为例
const config = [
{
id: 'id-1',
title: '姓名',
type: 'text',
value: '...'
},
{
id: 'id-2',
title: '职业',
type: 'text',
value: '...'
},
]
const groupJSX = <form.Group gid='g-1' data={config} />
const group = form.getGroup('g-1')
append 追加配置
const newConfig = {
id: 'id-new',
title: '...',
type: '',
value: ''
}
group.append(newConfig)
delete 删除配置
group.delete({id: 'id-2'})
insert 插入配置
group.insert(1, {title: '插入表单', type: '', value: ''})
以上只是举例列出相关的api方法,使用这些api方法我们能够对表单做灵活的修改
联动
很多场景中表单之间需要联动,可以通过配置来实现联动操作,引入一个联动属性union建立观察者与被观察者的关系
const textConfig = {id: 'id-1', title: '...', type: 'text', value: '...'}
const textWatcher = {
title: '观察者表单',
id: 'watcher',
type: 'text',
value: '...',
union: [
'id-1',
'onChange',
function(value){
/* do something */
}
]
}
union属性,包含观察对象,观察对象方法名,响应方法,通过union属性建立了不同表单之间的关系, 当id-1表单值change时将触发观察者的响应方法
可扩展
表单组件可以通过register方法注册新的表单,以适应不同的表单交互需求,比如下例
// cell默认支持text表单(部分)
// 注册一个自定义text表单,替换内置text表单,拥有更丰富的表单类型
function TextInput(props) {
let $props = { ...props };
$props.store = null;
$props.registeroptions = null;
return <input {...$props} />;
}
register(
"Text",
[
"text",
"number",
"telephone",
"password",
"color",
"email",
"date",
"time",
"range",
"month"
],
TextInput
);
const textConfig = {type: 'month'}
const form = <form.Text data={textConfig} />
上例定义了一个text类型的表单,当type类型落在自定义表单的范围内,就会使用预定的react方法组件来输出结构,我们还可以为该方法组件定义一些api方法,以灵活控制表单
写在最后
经过一个多月陆陆续续的开发,已完成一个版本的表单组件,基本实现上述功能