2

container表单组件

在实际的项目中,JSON表单提供的表单组件是远远不够的,而且提供表单组件是一件低效的事,目前Ant Design组件库提供的表单组件就已经很实用了。

所以container提供了一套规则来自定义表单组件,来满足实际项目中复杂且灵活的表单组件使用场景,container主要的作用有以下几点:

  1. 自定义表单组件,例如图片上传组件
  2. 添加表单组件库,例如Ant-Design
  3. 处理控制逻辑和联动逻辑

自定义input组件

import {Input} from 'antd'
{
    formKey: 'test-form',
    ...
    config: [
        {
            type: 'container',
            dataKey: 'name',
            label: 'Param',
            customConfig: {
                // 自定义的配置
            },
            render: (curData, config, {changeFn, changeDataFn, getFocus, loseFocus, JSONForm, error, assistData, data}) => {
                return <Input value={curData} 
                    {...config.customConfig} 
                    onFocus={getFocus}
                    onBlur={loseFocus}
                    placeholder={config.placeholder ? config.placeholder : ''}
                    style={{borderColor: !!error ? '#f5222d' : ''}}
                    onChange={event => changeFn(event.target.value)} />
        }
        }
    ]
}

render方法

render方法的参数:
    1:curData,该container组件对应的值
    2:config,该container的组件配置,config.customConfig是自定义配置,里面可以传入antd的input组件的配置
    3:changeFn,changeDataFn:提交数据的方法, 两者的区别在于changeFn只能提交当前表单组件的值,changeDataFn能自定义提交的值
        changeFn(value, [callback])
        changeDataFn(key, value, [callback])
    4:getFocus,loseFocus:用来触发数据校验,loseFocus方法触发校验,getFocus方法来取消报错信息
    5:JSONForm是用来在render方法里渲染组件配置,即在container里嵌套组件配置
    6:error:校验报错
    7:assistData,data: 逻辑数据和表单数据

表单组件库

其实你会发现,container自定义的表单组件并不比原始表单简单,估计你会怀疑这种实现方式的价值。的确,如果container只能这样自定义使用我们的表单组件,那么它的实用意义的确不大。

在我的团队项目中,大家使用的都是Ant-Design组件库,那么接下来我们就把Ant-Design组件库接入到JSON表单中。

首先我们创建一个组件文件,取名为antd-components.js:

import React from 'react'
import { Input } from 'antd'
export default [
    {
        type: 'antd-input',    // 声明为antd-input的自定义表单组件
        render: (curData, config, {changeFn, getFocus, loseFocus, error}) => {
            return <Input value={curData}  
            onFocus={getFocus}
            {...config.customConfig}
            onBlur={loseFocus}
            placeholder={config.placeholder ? config.placeholder : ''}
            style={{borderColor: !!error ? '#f5222d' : ''}}
            onChange={event => changeFn(event.target.value)} />
        }
    }
]

然后在我们的项目初始化的文件中(init.js)引入该组件库:

import Form from 'json_transform_form'
import components from './antd-components'
From.createCustomComp(components)

这样我们就可以在项目的任何地方使用该组件库:

{
    formKey: 'test-form',
    ...
    config: [
        {
            type: 'antd-input',  // 使用antd-input表单组件
            dataKey: 'name',
            label: 'Param',
            customConfig: {},   // 自定义配置
        }
    ]
}

你看这样container的实用价值就体现出来了,复杂表单组件的自定义只需要编写一次,接下来的重复使用,只需要传入相应的配置即可。

跨项目的共用表单组件也是通过该方式实现,维护一个不同项目都可引用的组件库文件,将常用的复杂表单组件,抽象在该组件库文件里,然后在不同项目初始化时引入进来,这样就能在不同项目中共用表单组件。

modifyDataFn

通过container使用共用表单组件时,存在一个问题,那就是无法再次自定义表单组件的提交事件,例如:使用上面的antd-input自定义组件,该组件自动提交本组件的数据,但是如果想联动处理其他表单,修改其他表单组件的数据,这个时候不能在组件配置里重写render,因为组件配置里的render会覆盖掉组件库中的render,导致抽象出来的渲染方法失效。

modifyDataFn用来自定义提交数据,只会覆盖render方法中的提交数据的功能。

{
    formKey: 'test-form',
    ...
    config: [
        {
            type: 'antd-input',  // 使用antd-input表单组件
            ...
            modifyDataFn: ({changeFn, changeDataFn}, {parent, self}) => {
                changeDataFn('name', self.curDAta)
            }
        }
    ]
}

modifyDataFn的参数:

   1:changeFn,changeDataF,提交数据的方法
   2:parent,当该表单组件为form_array的子表单组件时,该值为form_array的组件数据
   3: self,该表单组件的数据

处理控制逻辑和联动逻辑

在JSON表单的表单配置中,有assistData的选填字段,该字段为JSON表单内部处理复杂的控制逻辑所需的额外数据,该数据不会被提交和缓存。例如:在表单内存在一个刷新按钮,点击会刷新前一个表单组件的数据,其效果图如下:
图片描述
表单中间的刷新按钮,可以认为是一个特殊的container表单组件,因此可以根据container来自定义该刷新按钮:

{
    formKey: 'test-form',
    assistData: {
        refresh: false,
    },
    config: [
        ...
        {
            type: 'container',
            dataKey: 'assistData.refreshParam',
            style: {
                ...
            },
            render: (curData, config, {changeFn, changeDataFn}) => {
                const handleClick = () => {
                    changeDataFn('assistData.refresh' ,true)
                    setTimeout(() => {
                        changeDataFn('assistData.refresh' ,false)
                    }, 1000 * 3)
                }
                return <React.Fragment>
                    {
                        config.index === config.parentData.length - 1 &&
                        <Popover placement="top" content="刷新param列表">
                            <Button shape="circle" loading={curData} onClick={handleClick}>{!curData && <Icon type="reload" />}</Button>
                        </Popover>
                    }
                </React.Fragment>
            }
        },
    ]
}

上面的代码实现了刷新按钮点击刷新的动作,其刷新逻辑是assistData里的refresh字段控制。

注意:如果要使用assistData中的数据,其dataKey必须以assistData开头,且必须使用changeDataFn自定义提交assistData数据。

container嵌套组件配置

如果container表单组件里还含有其他表单组件,这时直接通过组件配置去渲染无疑能节约不少的工作量。

{
    data: {
        param: {
            name: ''
        }
    },
    config: [
        {
            type: 'container',
            dataKey: 'param',
            render: (curData, config, {changeFn, changeDataFn, JSONForm}) => {
                return <div>
                    {
                        JSONForm([
                            {
                                type: 'input',
                                dataKey: 'name',
                                placeholder: '请输入param',
                                validate: ['required'],
                            }
                        ])
                    }
                </div>
            }
    ]
}

JSONForm方法传入组件配置的列表就能渲染出表单组件来,需要注意的是,子表单组件的dataKey一定是基于父表单组件的。

JSON表单的实例方法请看下节的JSON生成Form表单(四)


熟悉的陌生人
330 声望17 粉丝

如果我有一本书...