Form development mode

Posted by Scabby on Mon, 17 Jan 2022 11:57:36 +0100

If you master value/onChange, you will master the essence of form writing.

The management side component has only two purposes:

  1. Collect data from users
  2. Show data to users

There are three data formats for collecting data from users:

  1. The value of the original data type (such as string/number, etc.)
  2. object
  3. array

Therefore, the development mode of collecting any data from users is:

  1. Three input boxes, one interface: value/onChange.
    1. Input box for collecting raw data type (provided by most antd)
    2. Input box for collection object
    3. Collection array input box
  2. Form is a convenient tool for distributing objects to the next level of input; The form itself is the input of an output object to the upper level.
  3. The development of collecting nested object forms is to descend level by level. The development can collect the input of each object. This is a recursive process and can be automated.

example

Collect non nested objects

Collect the following objects:

{
  "name": "Reward rule name",
  "desc": "Description of reward rules"
}

Directly use the antd form and distribute it to the input collecting the original value:

Form:
  Input: name
  Input: desc

Collect nested objects

Collect the following objects:

{
  "name": "Reward rule name",
  "desc": "Description of reward rules"
  "msg": {
    "template_id": "323ll1w",
    "app_id": "app12323"
  }
}

Using the antd form, when distributing to msg:

Form:
  Input: name
  Input: desc
  An input box that can collect objects: msg

We need an input box that can collect objects, and its interface conforms to value/onChange.

Review article 2 of the development model:

Form is a convenient tool for distributing objects to the next level of input; The form itself is the input of an output object to the upper level.

The form itself is a component that can output objects. Just change its interface to value/onChange.

Input box components that can collect msg objects:

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: 'Template 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>
  )
}

This [input box that can collect msg objects] is not far from becoming [input box that can collect any object], for example:

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>
      )
  }
}

Collect nested array objects

For example:

{
  "name": "Reward rule name",
  "desc": "Description of reward rules"
  "msg": {
    "template_id": "323ll1w",
    "app_id": "app12323"
  },
  "msgs": [
    {
      "template_id": "323ll1w",
      "app_id": "app12323"
    },
    {
      "template_id": "323ll1w",
      "app_id": "app12323"
    },
  ]
}

Using the antd form, when distributing to msgs:

Form:
  Input: name
  Input: desc
  MsgInput: msg
  An input box that can collect arrays: msgs

Input box components that can collect msgs arrays:

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) {
    // If it is empty, notify the external first, change it to an empty array, and render at least one table
    onChange([])
    return <Button type='primary' onClick={add}>
      newly added
    </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}>
        newly added
      </Button>
      {(value?.length > 1) ? <Button type='default' onClick={del}>
        delete
      </Button> : ''}
    </>
  )
}

This [input box that can collect msgs arrays] is not far from becoming [input box that can collect any array], for example:

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) {
      // If it is empty, notify the external first, change it to an empty array, and render at least one table
      onChange([])
      return <Button type='primary' onClick={add}>
        newly added
      </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}>
          newly added
        </Button>
        {(value?.length > 1) ? <Button type='default' onClick={del}>
          delete
        </Button> : ''}
      </>
    )
  }
}

follow-up

If the three input boxes can be generalized and the mode is fixed, can the form be output automatically given a data to be collected?

To be broken down next time.

Extension: sharing and reuse mechanism

The key of reuse is classification.

  1. Put your needs into a category,
  2. Then, according to this category, find the components that meet the requirements.

Therefore, the essence of classification is: a technology for fast positioning solutions.

When providing a range of components, the most important thing is to establish a set of classification criteria:

  1. Each category has a clear boundary, which is mutually exclusive and does not overlap
  2. Concise and limited classification steps can quickly classify a requirement.

The greatest significance of this development model lies in:

The classification criteria for the data to be collected as components are established - clear and direct.

It is clear that it is possible to classify and match program execution:

Input a data and automatically match the corresponding type of components by inferring the type.

Topics: React