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