Ant.Design 表单联动

6,033 阅读1分钟

简介

在使用 Ant.Design 构建复杂表单的时候,常常需要表单项根据用户的输入而动态变化。本文列举了作者在实际开发中积累的最佳实践,主要列举了以下两个场景:

  1. 表单项之前存在依赖关系
  2. 动态增减不同类别的表单项

表单项之前存在依赖关系

CPT2202171723-440x260.gif

实现代码如下:

<Form>
    <Form.Item name="type">
      <Radio.Group>
        <Radio value="text">文本模块</Radio>
        <Radio value="image">图片模块</Radio>
      </Radio.Group>
    </Form.Item>

    <Form.Item dependencies={['type']} noStyle>
      {({ getFieldValue }) => {
        const type = getFieldValue('type')

        if (type === 'text') {
          return (
            <>
              <Form.Item label="文本内容" name="text">
                <Input placeholder="请输入文本内容" />
              </Form.Item>
              <Form.Item label="文本颜色" name="textColor">
                <ColorInput placeholder="请输入文本颜色" />
              </Form.Item>
            </>
          )
        }

        if (type === 'image') {
          return (
            <Form.Item label="图片内容" name="image">
              <UploadSingle type="image" />
            </Form.Item>
          )
        }

        return null
      }}
    </Form.Item>
  </Form>

其中,type表单项的选择会影响到下面表单项的展示。 我们将受影响的表单项使用 <Form.Item dependencies={['type']} noStyle> 的标签包裹起来,使用 props render 来渲染它们。 dependenciesprops render同时使用表示:在dependencies 的表单项数据发生变化时重新渲染 props render 内的内容。详见官方文档: dependencies

动态增减不同类别的表单项

CPT2202171745-464x685.gif

官方文档提供的示例演示了基本的 Form.List 的使用方法,但如果想实现增减不同类别的一组表单,我们需要用到Form.List APIchildren渲染函数的 add 方法。

export interface FormListOperation {
    add: (defaultValue?: StoreValue, insertIndex?: number) => void;
    // ...
}

add 方法的第一个参数可以指定添加的这一组表单项的初始值。然后结合上一节介绍的知识,我们就可以实现动态增减不同类别的表单项了。

实现代码如下:

<Form.Item label="模块列表" required>
  <Form.List name="moduleList">
    {(fields, { add, remove }) => (
      <>
        {fields.map(({ key, name, ...restField }, index) => (
          <React.Fragment key={key}>
            <Dropdown
              key={`Dropdown-${key}`}
              trigger={['click']}
              overlay={
                <Menu onClick={(e) => add({ type: e.key }, index)}>
                  <Menu.Item key="text">文本模块</Menu.Item>
                  <Menu.Item key="image">图片模块</Menu.Item>
                </Menu>
              }
            >
              <Button type="dashed" block icon={<PlusCircleOutlined />}>
                添加一个模块
              </Button>
            </Dropdown>
            <div key={`Items-${key}`} className="landing-form-block">
              <Form.Item {...restField} label="模块类型" name={[name, 'type']}>
                <Select disabled>
                  <Select.Option value="text">文本模块</Select.Option>
                  <Select.Option value="image">图片模块</Select.Option>
                </Select>
              </Form.Item>

              <Form.Item dependencies={[[name, 'type']]} noStyle>
                {({ getFieldValue }) => {
                  const type = getFieldValue(['moduleList', name, 'type'])

                  if (type === 'text') {
                    return (
                      <>
                        <Form.Item label="文本内容" name="text">
                          <Input placeholder="请输入文本内容" />
                        </Form.Item>
                        <Form.Item label="文本颜色" name="textColor">
                          <ColorInput placeholder="请输入文本颜色" />
                        </Form.Item>
                      </>
                    )
                  }

                  if (type === 'image') {
                    return (
                      <Form.Item label="图片内容" name="image">
                        <UploadSingle type="image" />
                      </Form.Item>
                    )
                  }

                  console.warn('unknown type: ', type)

                  return null
                }}
              </Form.Item>

              <Button
                style={{ width: '100%' }}
                type="primary"
                danger
                size="small"
                onClick={() => remove(name)}
                icon={<DeleteOutlined />}
              >
                删除
              </Button>
            </div>
          </React.Fragment>
        ))}
        <Dropdown
          trigger={['click']}
          overlay={
            <Menu onClick={(e) => add({ type: e.key })}>
              <Menu.Item key="text">文本模块</Menu.Item>
              <Menu.Item key="image">图片模块</Menu.Item>
            </Menu>
          }
        >
          <Button type="dashed" block icon={<PlusCircleOutlined />}>
            添加一个模块
          </Button>
        </Dropdown>
      </>
    )}
  </Form.List>