前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >表单开发模式

表单开发模式

原创
作者头像
joefu
发布2022-01-16 12:16:47
8444
发布2022-01-16 12:16:47
举报
文章被收录于专栏:前端开发的「术」

掌握了 value/onChange,就掌握了表单编写的精髓。

管理端的组件,只有两个目的:

  1. 向用户搜集数据
  2. 向用户展示数据

向用户搜集数据,存在三种数据格式:

  1. 原始数据类型的值(比如 string/number 等)
  2. 对象
  3. 数组

所以,向用户搜集任何数据的开发模式是:

  1. 三种输入框,一种接口:value/onChange。
    1. 搜集原始数据类型的输入框(大部分 antd 提供)
    2. 搜集对象的输入框
    3. 搜集数组的输入框
  2. form 是分发对象到下一级 input 的便利工具;form 本身对上一级是一个输出对象的 input。
  3. 搜集嵌套对象表单的开发,就是逐级下降,开发能搜集每个对象的 input。这是一个递归的过程,而且能够被自动化。

例子

搜集无嵌套对象

搜集下面的对象:

代码语言:json
复制
{
  "name": "奖励规则名",
  "desc": "奖励规则说明"
}

直接用 antd form,分发到搜集原始值的 input 即可:

代码语言:text
复制
Form:
  Input: name
  Input: desc

搜集嵌套对象

搜集下面的对象:

代码语言:json
复制
{
  "name": "奖励规则名",
  "desc": "奖励规则说明"
  "msg": {
    "template_id": "323ll1w",
    "app_id": "app12323"
  }
}

用 antd form,分发到 msg 时:

代码语言:text
复制
Form:
  Input: name
  Input: desc
  一个能搜集对象的输入框: msg

我们需要一个能搜集对象的输入框,而且它的接口符合 value/onChange。

回顾开发模式第二条:

form 是分发对象到下一级 input 的便利工具;form 本身对上一级是一个输出对象的 input。

form 本身就是一个可以输出对象的组件,只需要把它的接口改造成 value/onChange 即可。

能搜集 msg 对象的输入框组件:

代码语言:javascript
复制
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 对象的输入框】,离成为【搜集任何对象的输入框】已经不远,比如:

代码语言:javascript
复制
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>
      )
  }
}

搜集嵌套数组的对象

比如:

代码语言:json
复制
{
  "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 时:

代码语言:text
复制
Form:
  Input: name
  Input: desc
  MsgInput: msg
  一个能搜集数组的输入框: msgs

能搜集 msgs 数组的输入框组件:

代码语言:javascript
复制
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 数组的输入框】,离成为【搜集任何数组的输入框】已经不远,比如:

代码语言:javascript
复制
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> : ''}
      </>
    )
  }
}

后续

如果三种输入框都可以通用化,而且模式固定,那么,给定一个待搜集的数据,能自动化输出表单吗?

待下回分解。

拓展:共享和复用机制

复用的关键是:分类。

  1. 把自己的需求,落到一个类别,
  2. 然后根据这个类别,找到符合需求的组件。

所以,分类的本质是:快速定位解决方案的一种技术。

当提供一系列组件时,最重要的是确立了一套分类标准:

  1. 每个类别具有明确的边界,互斥不重叠
  2. 简明有限的分类步骤,能对一个需求执行快速分类。

而这个开发模式最大的意义在于:

确立了以待搜集数据作为组件的分类标准 —— 明确而且直接。

明确到,具备程序执行分类和匹配的可能:

输入一个数据,通过推断类型,自动匹配能输出相应类型的组件。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 例子
    • 搜集无嵌套对象
      • 搜集嵌套对象
        • 搜集嵌套数组的对象
        • 后续
        • 拓展:共享和复用机制
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档