掌握了 value/onChange,就掌握了表单编写的精髓。
管理端的组件,只有两个目的:
- 向用户搜集数据
- 向用户展示数据
向用户搜集数据,存在三种数据格式:
- 原始数据类型的值(比如 string/number 等)
- 对象
- 数组
所以,向用户搜集任何数据的开发模式是:
- 三种输入框,一种接口:value/onChange。
- 搜集原始数据类型的输入框(大部分 antd 提供)
- 搜集对象的输入框
- 搜集数组的输入框
- form 是分发对象到下一级 input 的便利工具;form 本身对上一级是一个输出对象的 input。
- 搜集嵌套对象表单的开发,就是逐级下降,开发能搜集每个对象的 input。这是一个递归的过程,而且能够被自动化。
例子
搜集无嵌套对象
搜集下面的对象:
{
"name": "奖励规则名",
"desc": "奖励规则说明"
}
直接用 antd form,分发到搜集原始值的 input 即可:
Form:
Input: name
Input: desc
搜集嵌套对象
搜集下面的对象:
{
"name": "奖励规则名",
"desc": "奖励规则说明"
"msg": {
"template_id": "323ll1w",
"app_id": "app12323"
}
}
用 antd form,分发到 msg 时:
Form:
Input: name
Input: desc
一个能搜集对象的输入框: msg
我们需要一个能搜集对象的输入框,而且它的接口符合 value/onChange。
回顾开发模式第二条:
form 是分发对象到下一级 input 的便利工具;form 本身对上一级是一个输出对象的 input。
form 本身就是一个可以输出对象的组件,只需要把它的接口改造成 value/onChange 即可。
能搜集 msg 对象的输入框组件:
import { Form, Input } from 'antd'
import React, { FC } from 'react'
export const MsgInput: FC<{
value?: any,
onChange?: (value: string) => void
}> = (props) => {
const { value, onChange = (() => { }) } = props
const [form] = Form.useForm()
const fields = [{
label: '模板 ID',
key: 'template_id',
jsx: <Input />,
}, {
label: 'APPID',
key: 'app_id',
jsx: <Input />,
}]
const getFields = (fields) => {
const children = fields.map(one => (
<Form.Item
name={one.key}
label={one.label}
rules={one.rules}
>
{one.jsx ? one.jsx : <Input />}
</Form.Item>
))
return children
}
return (
<Form
form={form}
initialValues={value}
name='object_input'
onValuesChange={(_, values) => {
onChange(values)
}}
>
{getFields(fields)}
</Form>
)
}
而这个【能搜集 msg 对象的输入框】,离成为【搜集任何对象的输入框】已经不远,比如:
export const CreateObjectInput = (fields) => {
return (props) => {
const { value, onChange = (() => { }) } = props
const [form] = Form.useForm()
const getFields = (fields) => {
const children = fields.map(one => (
<Form.Item
name={one.key}
label={one.label}
rules={one.rules}
>
{one.jsx ? one.jsx : <Input />}
</Form.Item>
))
return children
}
return (
<Form
form={form}
initialValues={value}
name='object_input'
onValuesChange={(_, values) => {
onChange(values)
}}
>
{getFields(fields)}
</Form>
)
}
}
搜集嵌套数组的对象
比如:
{
"name": "奖励规则名",
"desc": "奖励规则说明"
"msg": {
"template_id": "323ll1w",
"app_id": "app12323"
},
"msgs": [
{
"template_id": "323ll1w",
"app_id": "app12323"
},
{
"template_id": "323ll1w",
"app_id": "app12323"
},
]
}
用 antd form,分发到 msgs 时:
Form:
Input: name
Input: desc
MsgInput: msg
一个能搜集数组的输入框: msgs
能搜集 msgs 数组的输入框组件:
import { Form } from 'antd'
import React, { FC } from 'react'
export const MsgsInput: FC<{
value?: any,
onChange?: (value: any) => void
}> = (props) => {
const { value, onChange = (() => { }) } = props
const add = () => {
onChange([...value, {}])
}
const del = () => {
const newVal = [...value]
newVal.pop()
onChange(newVal)
}
if (!value) {
// 如果为空,先通知外部,改为空数组,至少渲染一个表格
onChange([])
return <Button type='primary' onClick={add}>
新增
</Button>
}
const onOneInputChange = (v, i) => {
const copy = [...value]
copy[i] = v
onChange(copy)
}
return (
<>
{value.map((one, index) => <MsgInput key={index} value={one} onChange={(value) => onOneInputChange(value, index)} />)}
<Button type='primary' onClick={add}>
新增
</Button>
{(value?.length > 1) ? <Button type='default' onClick={del}>
删除
</Button> : ''}
</>
)
}
而这个【能搜集 msgs 数组的输入框】,离成为【搜集任何数组的输入框】已经不远,比如:
export const CreateArrayInput = (OneInput) => {
return (props) => {
const { value, onChange = (() => { }) } = props
const add = () => {
onChange([...value, {}])
}
const del = () => {
const newVal = [...value]
newVal.pop()
onChange(newVal)
}
if (!value) {
// 如果为空,先通知外部,改为空数组,至少渲染一个表格
onChange([])
return <Button type='primary' onClick={add}>
新增
</Button>
}
const onOneInputChange = (v, i) => {
const copy = [...value]
copy[i] = v
onChange(copy)
}
return (
<>
{value.map((one, index) => <OneInput key={index} value={one} onChange={(value) => onOneInputChange(value, index)} />)}
<Button type='primary' onClick={add}>
新增
</Button>
{(value?.length > 1) ? <Button type='default' onClick={del}>
删除
</Button> : ''}
</>
)
}
}
后续
如果三种输入框都可以通用化,而且模式固定,那么,给定一个待搜集的数据,能自动化输出表单吗?
待下回分解。
拓展:共享和复用机制
复用的关键是:分类。
- 把自己的需求,落到一个类别,
- 然后根据这个类别,找到符合需求的组件。
所以,分类的本质是:快速定位解决方案的一种技术。
当提供一系列组件时,最重要的是确立了一套分类标准:
- 每个类别具有明确的边界,互斥不重叠
- 简明有限的分类步骤,能对一个需求执行快速分类。
而这个开发模式最大的意义在于:
确立了以待搜集数据作为组件的分类标准 —— 明确而且直接。
明确到,具备程序执行分类和匹配的可能:
输入一个数据,通过推断类型,自动匹配能输出相应类型的组件。
正文完