Antd v3 Form实现动态添加表单项及数据回显

6,247 阅读2分钟

0、需求

Form表单需要有新增和编辑表单项的功能,编辑即需要有表单有数据回显的功能。

环境:

react:16.13.1;

Antd 3.x。

一、创建表单

1. create

Form.create()

这是一个高阶函数,传入的是react组件,返回一个新的react组件,在函数内部会对传入组件进行改造。

经 Form.create() 包装过的组件会自带 this.props.form 属性。

2. 表单数据绑定

getFieldDecorator(id, options)

用于和表单进行双向绑定

<!-- 表单数据绑定 -->
<Form.Item {...formItemLayout} label={'名称'}>
    {getFieldDecorator('searchName')(
        <Input placeholder={'请输入名称'} />
    )}
</Form.Item>

3. 渲染查询表单的查询条件

render <Form.Item>

4. 获取查询条件的值

form.validateFields / validateFieldsAndScroll

校验并获取一组输入域的值与 Error,若 fieldNames 参数为空,则校验全部组件

const { form } = this.props;

// 获取并检查表单数据
form.validateFields((err, fieldsValue) => {
      if (err) return;
      const { searchName = '' } = fieldsValue;
});

二、实现动态表单

1. 效果图:

  • 初始状态:

  • 点击 + 后,

  • 增加两条表单项后,出现删除icon。

2. 实现add和remove方法。

其中的 keys 是唯一值,官方demo中使用的 id++,这里改写了个random值。

function add(props) {
    const { form } = props;
    // can use data-binding to get
    const keys = form.getFieldValue('keys');
    const nextKeys = keys && keys.concat(Math.floor(Math.random() * 1000) + 1);
    // can use data-binding to set
    // important! notify form to detect changes
    form.setFieldsValue({
        keys: nextKeys,
    });
}

function remove(props, k) {
    const { form } = props;
    // can use data-binding to get
    const keys = form.getFieldValue('keys');
    // We need at least one item
    if (keys && keys.length === 1) {
        return;
    }
    form.setFieldsValue({
        keys: keys.filter(key => key !== k),
    });
}

3. 从上层组件获取数据

通过使用 onFieldsChange 与 mapPropsToFields,可以把表单的数据存储到上层组件或者 Redux、dva 中。

  • onFieldsChange: 当 Form.Item 子节点的值发生改变时触发;
  • mapPropsToFields:把父组件的属性映射到表单项上。

mapPropsToFields 里面返回的表单域数据必须使用 Form.createFormField 包装。

如下代码中,mapPropsToFields 中 return 的字段 key 名 与 render 中 getFieldDecorator 所包含的id名一一对应,如例子中的 "username”、”keys”。

const CustomForm = Form.create({
    name: 'global_state',

    onFieldsChange(props, changedFields, allFields) {
        props.onChange(allFields); // changedFields
    },

    mapPropsToFields(props) {
        return {
            username: Form.createFormField({
                ...props.username,
                value: props.username.value,
            }),

            keys: Form.createFormField({
                ...props.keys,
                value: props.keys && props.keys.value,
            }),

            names: props.names && props.names.map(k => (
                Form.createFormField({
                    ...props.names,
                    value: k && k.value,
                })
            )),

            phones: props.phones && props.phones.map(k => (
                Form.createFormField({
                    ...props.phones,
                    value: k && k.value,
                })
            )),
        };
    },

    onValuesChange(props, changedValues, allValues) {
        console.log(changedValues);
    },

})(props => {
    const { getFieldDecorator, getFieldValue } = props.form;

    getFieldDecorator('keys', { initialValue: [] });
    const keys = getFieldValue('keys') || [];

    const addItems = keys && keys.map((k, index) => (
        <Row gutter={8} key={index}>
            <Col xs={24} sm={12} md={12} lg={12} xl={12}>
                <Form.Item
                    label="姓名"
                    required={false}
                    key={k}
                >
                    {getFieldDecorator(`names[${index}]`, {
                        validateTrigger: ['onChange', 'onBlur'],
                        rules: [
                            {
                                whitespace: true,
                            },
                        ],
                    })(<Input style={{ width: '60%', marginRight: 8 }} />)}
                </Form.Item>
            </Col>
            <Col xs={24} sm={12} md={12} lg={12} xl={12}>
                <Form.Item
                    label="手机号码"
                    required={false}
                    key={k}
                >
                    {getFieldDecorator(`phones[${index}]`, {
                        validateTrigger: ['onChange', 'onBlur'],
                        rules: [
                            {
                                whitespace: true,
                            },
                        ],
                    })(<Input style={{ width: '80%', marginRight: 20 }} />)}
                    {keys.length > 1 ? (
                        <Icon
                            className="dynamic-delete-button"
                            type="minus-circle-o"
                            onClick={() => remove(props, k)}
                        />
                    ) : null}
                </Form.Item>
            </Col>
        </Row>
    ));

    return (
        <Form
            onSubmit={props.onHandleSubmit}
            gutter={{ xs: 8, sm: 16, md: 24, lg: 32 }}
            style={{ marginBottom: 0 }}
        >
            <Form.Item label="Username">
                {getFieldDecorator('username', {
                    rules: [{ required: true, message: 'Username is required!' }],
                })(<Input />)}
            </Form.Item>

            <Form.Item>
                <div>
                    <p style={{ display: 'inline-block', margin: '0 10px' }}>添加一条信息</p>
                    <Icon type="plus" onClick={() => add(props)} />
                </div>
            </Form.Item>

            { addItems}

        </Form>
    );
});

4. 上层组件

export default class MyForm extends React.Component<any, any> {

    state = {
        fields: {
        	// 与表单 mapPropsToFields return的 key 一致
            username: {
                value: '',
            },
            keys: {
                value: [],
            },
            names: [],
            phones: [],
        },
    };
    
    handleSubmit = e => {
        e.preventDefault();
    }

    handleCancel = () => {
        // this.props.dismissModal();
    }

    handleFormChange = (changedFields) => {
        this.setState(({ fields }) => ({ // 更新表单字段值
            fields: { ...fields, ...changedFields },
        }));
    };

    render() {
        const { fields } = this.state;

        return (
            <>
                <CustomForm
                    {...fields}
                    onChange={this.handleFormChange}
                    onHandleSubmit={this.handleSubmit}
                    onHandleCancle={this.handleCancel}
                />
            </>
        );
    }
}

ref

  1. www.yuque.com/lulu27753/a…
  2. juejin.cn/post/684490…