前言
ErgateForm
是配置化的 antd form(react)
组件。通过配置化生成表单。实现了表单联动、动态表等单功能,表单属性仍沿用 antd form 各个组件的属性。
我自己在做ToB类项目时,比较头疼大量的表单业务。会将表单等组件重新封装一下,使用JSON来配置化自动生成表单。即方便CV,也方便各种抽离,对于模块化也比较友好,下图是我做的DEMO,可以很方便的组合成各种表单
基础使用
安装
npm install @aotoo/ergateform
# or
yarn add @aotoo/ergateform
基础表单
import ErgateForm from '@aotoo/ergateform'
import {Form} from 'antd'
function App(){
const form = Form.useForm()
const formConfig = {
labelCol:{ span: 8 }
wrapperCol: { span: 16 }
layout: "horizontal"
initialValues: {textbox: 'hello world'}
form: form // antd的form实例
data: [
{
label: '文本框',
name: 'textbox',
$input: {
type: 'text', // antd Input
},
},
]
}
return (
<ErgateForm {...formConfig}/>
)
}
联动表单
import ErgateForm from '@aotoo/ergateform'
import {Form} from 'antd'
function App(){
const form = Form.useForm() // antd的form实例
const formConfig = {
labelCol:{ span: 8 }
wrapperCol: { span: 16 }
layout: "horizontal"
initialValues: {textbox: 'hello world'}
form: form
data: [
{
label: '目标文本框',
name: 'target-input',
$input: { type: 'text'},
},
{
label: '响应文本框',
name: 'response-input',
$input: { type: 'text' },
union: {
target: 'target-input',
event: 'onChange',
callback(e){
console.log(e.target.value)
form.setFieldValue('response-input', e.target.value)
}
}
},
]
}
return (
<ErgateForm {...formConfig}/>
)
}
联动结构
import ErgateForm from '@aotoo/ergateform'
import {Form} from 'antd'
function App(){
const form = Form.useForm()
const formConfig = {
labelCol:{ span: 8 }
wrapperCol: { span: 16 }
layout: "horizontal"
initialValues: {textbox: 'hello world'}
form: form // antd的form实例
data: [
{
label: '文本框',
name: 'select-box',
$input: {
type: 'select',
options: [
{label: '选项一', value: 'option-1'},
{label: '选项二', value: 'option-2'},
{label: '选项三', value: 'option-3'},
]
},
},
union('select-box', function(value){
if (value === 'option-2') {
return JSX
}
})
]
}
return (
<ErgateForm {...formConfig}/>
)
}
联动状态
import ErgateForm from '@aotoo/ergateform'
import {Form} from 'antd'
function App(){
const form = Form.useForm()
const formConfig = {
labelCol:{ span: 8 }
wrapperCol: { span: 16 }
layout: "horizontal"
initialValues: {textbox: 'hello world'}
form: form // antd的form实例
state: { visible: false },
data: function(state, setState){
return [
<button onClick={function(){
setState({
visible: true
})
}}>按钮 </button>,
union('state.visible', function(value){
if (value) {
return JSX
}
})
]
}
}
return (
<ErgateForm {...formConfig}/>
)
}
支持的表单类型
{
$input: { type: '???', } // type 用来设置表单类型
}
- button => Button
- text => Input 表单的别名
- textarea => Input.TextArea 别名
- search => Input.Search
- password => Input.Password
- cascader => Cascader
- select => Select
- autocomplete => AutoComplete
- inputnumber => InputNumber
- rate => Rate
- slider => Slider
- switch => Switch
- timepicker => TimePicker
- timerange => TimePicker.RangePicker 别名
- treeselect => TreeSelect
- datepicker => DatePicker
- daterange => DatePicker.RangePicker 别名
- checkbox => Checkbox
- checkboxGroup => Checkbox.Group
- radio => Radio
- radioGroup => Radio.Group
- transfer => Transfer
- upload => Upload
配置
Form配置
在ErgateForm
中使用最外层的非data
属性来配置Form属性
Form.state
这是ErgateForm的属性,原生antd Form表单无此属性,该属性作用是用来方便模块化时能够方便设置状态值得变更,使用React的useState实现
ergateform的写法
const formConfig = {
labelCol:{ span: 8 }
wrapperCol: { span: 16 }
layout: "horizontal"
...
data: [...] // data用来配置 Form.Item 集合
}
return <ErgateForm {...formConfig}/>
// 或者
return <ErgateForm
labelCol= {{span: 8}}
wrapperCol={{span: 16}}
layout={'horizontal'}
...
data: [...]
/>
antd form 原生写法
<Form
labelCol={{ span: 8 }}
wraperCol={{ span: 16 }}
layout="horizontal"
...
>
...
</Form>
FormItem配置
Form.Item 是antd表单的基础结构。ErgateForm使用data
的数据项来配置Form.Item
属性及表单属性
ergateform的写法
data为数组
// 配置 { data: [ { label: '标题名', // 对应 Form.Item 的label属性 name: 'uniq-name' // 对应 Form.Item 的name属性 $input: { ... } // 配置表单属性 }, JSX, union(...) ] }
- data为方法
可以将data设置为方法,只要返回数组项配置即可。该方法接收state, setState
两个参数,这样我们可以在配置中灵活设置一些状态
data: function(state, setState){
return [
...
]
}
数据项可以是JSX,或者union
方法返回的结构,union方法后面会讲到
antd form 原生写法
<Form.Item
label="标题名"
name="uniq-name"
>
<input {...$input} />
</Form.Input>
横向排列
ErgateForm 默认使用Space
组件来横向排列表单,只需要将$input
配置成数组即可,来看配置
{
data: [
{
..., // 此时这里的属性会自动添加到Space组件中
$input: [
{
label: 'UserName',
name: 'username',
$input: { // 嵌套表单仍然使用`$input`属性
type: 'text',
}
},
{
label: 'Password',
name: 'password',
$input: {
type: 'password'
}
}
]
}
]
}
radio / checkbox 的子项默认是横向排列,如果需要竖向排列时,官网是使用Space组件包裹子项,在这里直接配置$input.direction
就好了
特殊属性
ErgateFome
加入的属性,用来控制配置结构和联动
- $input
- union
$input
$input
用来配置具体的表单/表单组,如 Input, Select
等支持的表单元件,api 属性与官网一致
$input.type
该属性用来标识使用那个表单组件,
antd的Button组件包含type属性,请使用 buttonType
替换
union
用来设置表单联动,union的实现思路有点类似于监视者的角色,当目标值变更时及时做出响应。union属性包含三个必须设置的参数 target、event、callback
union.target
描述对齐的目标name
union.event
表单组件一般都有几个事件方法,例如 Search 表单组件有onSearch和onChange等事件,我们只想关注onChange事件时将union.event设置为onChange即可
union.callback
事件响应方法,当对齐目标表单状态发生改变时,触发该方法
多联动
一个表单需要关注多个状态变更时设置,将union设置为数组即可实现多联动
示例 code
[
{
label: '我是目标表单',
name: 'name-target',
$input: {type: 'select', ...}
},
// 单联动
{
label: '单联动响应表单',
name: 'name-response',
union: {
target: 'name-target',
event: 'onChange',
callback(value, option){
/** do something */
}
}
},
// 多联动
{
label: '多联动响应表单',
name: 'mul-response',
union: [
{target: 'name-target', event: 'onChange', callback: ...},
{target: 'name-target', event: 'onSearch', callback: ...},
]
}
]
union方法
在设计表单时,有些结构需要根据状态来显示,此时可以引入union方法来实现这种类型的需求。
三种用法
union有三种设置方法
- 表单值响应
原理是通过antd的form.useWatch
对观测表单的状态改变做出响应。需要注意,如果表单组件不包含value
属性,会提示报错。例如 Checkbox 组件就不包含 value 属性,需要通过state来控制Checkbox的状态。幸运的是其他所有表单都有value属性 - state值响应
- 静默响应,但会在表单完成时反馈出结构(useEffect实现)
// 联动组件
union('target-name', function(value){
return JSX
});
// 联动state
union('state.xxx', function(value){
return JSX
})
// 无响应
union('任意字串描述,不可以和组件name/state[name]重名即可', function(value){
return JSX
})
union方法中也可以设置其他表单组件的属性,但一定要加上延迟,否则会造成渲染冲突,这一点后面会讲到
示例CODE
注意下面的union方法的使用的位置
import ErgateForm {union} from '@aotoo/ergateform'
import {Form} from 'antd'
function App(){
const form = Form.useForm()
const formConfig = {
... // Form配置项
data: function(state, setState){
return [
{
$input: [ // 表单组
{
label: '文本框',
name: 'target-select',
$input: {
type: 'select',
options: [
{label: '选项一', value: '1'},
...
]
}
},
// 组内union
union('target-select', function(value){
if (value === '3'){
return <div>response vlaue 3</div>
}
})
],
},
<button>按钮</button>, // 支持直接插入JSX
// 组外union
union('target-input', function(value){
if (value === '1') {
return <div>response value 1</div>
}
if (value === '2') {
return <div>response value 2</div>
}
}),
// state联动
union('state.xxx', function(value){
return JSX
})
]
}
}
return <ErgateForm {...formConfig}/>
}
return <App />
union 方法不需要设置 event 参数,内部使用了 antd 的 Form.useWatch 实现值变更追踪
暂时不支持 async await promise 的使用
复杂使用
formItem方法
引入formItem方法,它用来创建单独的Form.Item结构,也可以用来创建联动表单结构
import {formItem, union} from '@aotoo/ergateform';
<ErgateForm
...
data={[
{..., name: 'select-box', $input: {type: 'select', options: [...]}},
union('select-box', function(value){
if (value === '???' ) {
return formItem({
label: '新表单',
...,
$input: { type: 'text'}
})
}
})
]}
/>
formList方法
引入formList方法,它用于创建一组动态增、删的表单组,formList可以参考antd官方的原版例子,并未做更多的修改
import {formItem, union, formList} from '@aotoo/ergateform';
<ErgateForm
...
data={[
{..., name: 'select-box', $input: {type: 'select', options: [...]}},
union('select-box', function(value){
if (value === '???' ) {
return formList({...}).render((fields, {add, remove}, {errolist}) => {
return (
<>
{
fields.map((field)=>{
return <input {...field}/>
})
}
<button onClick={add()} >新增</button>
</>
)
})
}
})
]}
/>
getForm方法
Ergate使用的是antd的最新版本(5),可以使用form的实例来做很多事情,getForm方法是为了模块化时能方便取到form实例,ErgateForm会拦截每个表单事件的回调方法,重构后并还原成原来的使用方式
data = [
{
label: 'title',
name: 'name',
$input: {
type: 'button',
buttonType: '...', // 注意button组件是没有value的,不能够使用union来追踪
onClick(){
const form = this.getForm() // 获取form的实例
form.setFieldValue('xxx-name', value)
}
}
}
]
setOptions方法
setOptions
这个方法是ergateform的扩展方法,可以动态设置Form中各个Select组件的下拉选项
data = [
{
label: 'title',
name: 'select-name',
$input: {
type: 'select',
options: []
}
},
{
$input: {
type: 'button',
onClick(){
const form = this.getForm()
form.setOptions('select-name', [
{label: '选项一', value: '1'},
{label: '选项二', value: '2'},
...
])
}
}
}
]
union 方法中设置其他表单属性
不建议在 union
的回调方法中设置其他表单的属性,如果非要设置请加上延迟方法,否则会造成渲染冲突
示例 code
{
data: [
...,
{
label: '选择框',
name: 'select-box',
$input: {
options: [
{lable: 'a', value: 'a'},
{lable: 'b', value: 'b'},
]
}
},
union('select-box', function(value){
const form = this.getForm()
if (value === 'b') {
setTimeout(()=>{
form.setOptions('select-box', [...])
}, 100)
}
return JSX || null
})
]
}
使用state
const stateData = {
checked: false
}
const ergateFomrConfig = {
layout: "horizontal"
onFinish: onFinish,
...
state: stateData,
data: function(state, setState){
return [
{
name: 'check-box',
$input: {
type: 'checkbox',
checked: state.checked,
onChange(){
setState({
checked: !state.checked
})
}
}
},
union('state.checked', function(value){
console.log(value)
})
]
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。