antd form表单处理自定义组件
使用 antd 中的 Form 组件时,若使用自定义组件时,保存的时候获取不到自定义组件中的值。
以下是从antd找到的解决思路
被设置了 name 属性的 Form.Item 包装的控件,表单控件会自动添加 value(或 valuePropName 指定的其他属性) onChange(或 trigger 指定的其他属性)
我们可以通过表单控件自动添加的onChage,来做数据收集同步。
父组件:
import React, { Fragment, useState, useEffect, forwardRef, useRef } from 'react';
import HeaderTable from './headerTable';
const BasicForm = forwardRef((props, ref) => {
const { record } = props;
const [ form ] = Form.useForm();
<Form
name="taskForm"
ref={ref}
form={form}
>
<Row gutter={20}>
<Col span={24}>
<Form.Item name="headers" label={'Header'}>
<HeaderTable ref={headerTableRef} record={record} />
</Form.Item>
</Col>
</Row>
</Form>
export default BasicForm;
子组件:
import React, { useState, useEffect, useRef, useImperativeHandle, forwardRef} from 'react';
import { Transfer, Divider, Table, Button, Input, message, Select } from 'antd';
import AddRowTable from '@components/AddRowTable';//Table组件
const { Option } = Select;
const DATATYPE = [
{
id: 1,
name: 'int'
}, {
id: 2,
name: 'varchar'
}
]
const HeaderTable = ( props, ref ) => {
const { record, isLook, onChange } = props;
const addRowTable = useRef();
const [ sourceData, setSourceData ] = useState([]);
const [ columns, setColumns ] = useState([]);
const [ targetKeys, setTargetKeys ] = useState([]);
const [ headerList, setHeaderList ] = useState([]);
useEffect(() => {
if (record && record.id) {//当编辑时将传来的数据赋值
setHeaderList(record.sourceInfoMap.headers)
}
}, [record])
//设置columns表头
useEffect(() => {
const columns = [
{
title: '参数名称',
dataIndex: 'name',
render: (text, record) => {
return (
<Input value={text} onChange={tableItemChange.bind(this, record, 'name')} />
);
}
},
{
title: '操作',
dataIndex: 'operation',
align: 'center',
render: (text, record, index) => {
return (
<a onClick={delHandle.bind(this, record, index)}>'Delete'</a>
);
}
}];
setColumns(columns);
},[])
// 添加一行
const handleAddTableRow = () => {
setHeaderList(headerList => [...headerList, {}]);
onChange(headerList => [...headerList, {}])
};
// 删除一行
const delHandle = (record, index) => {
setHeaderList(headerList => {
headerList.splice(index, 1);
onChange(headerList)
return [...headerList];
});
};
// 表格中数据更改
const tableItemChange = (data, keyField, evt) => {
const value = evt.target ? evt.target.value : evt;
data[keyField] = value;
onChange(headerList)
};
return <div>
<AddRowTable
ref={addRowTable}
dataSource={headerList}
columns={columns}
isCanAdd={isLook}
disabled={isLook}
addItem={handleAddTableRow.bind(this)}
/>
</div>
}
export default HeaderTable;
以上操作就可以在表单中使用自定义组件,表单也能统一获取值。
不仅仅input输入框可以这么操作,所有组件,只要你拿到值后,把值给 onChange 方法,都是可以统一获取值的。
antd form表单中嵌套自定义组件
当某些自定义的组合类组件,也希望能进行表单元素的校验,以及利用antd的form表单实例进行数据的修改或者数据获取,这招便可以派上用场啦~
表单部分
<Form.Item label="周期" labelAlign="right">
{getFieldDecorator('cycle', {
rules: [{ required: true, validator: checkCycle }],
initialValue: cycle,
})(<CycleInput cycleOptions={cycleOptions} />)}
</Form.Item>
表单部分可以引入自定义的校验逻辑,如上面的checkCycle :
可以像下面这样,通过Promise.reject()返回错误校验提示,通过Promise.resolve()正确通过校验。
const checkCycle = useCallback((_, value) => {
const format = value?.split(' ');
if (!format) return Promise.reject(new Error('周期不可为空!'));
if (format.length < 2 && format[0] === 'always') {
return Promise.resolve();
}
if (format[1] === 'undefined') {
return Promise.reject(new Error('请选择周期!'));
}
const num = +format[0];
if (num > 0 && format[1]) {
return Promise.resolve();
}
return Promise.reject(new Error('请输入大于 0 的数字!'));
}, []);
有关校验的内容,antd也是参考一个成熟的库 async-validator,上面这种写法可以参考文档这块 asyncValidator
如果懒得看文档直接看图:
大概意思就是说,可以通过两种方式来实现一些异步校验:
(1)可以通过调用callback,即asyncValidator的第三个参数,来完成校验
(2)可以通过返回Promise,来完成校验
自定义组件
注意value是该表单域的值,onChange是对应可以改变该表单域的值的方法
const CycleInput = ({ value, onChange, cycleOptions }) => {
const format = value?.split?.(' ');
const num = format?.[0];
const type = format?.[1];
const triggerChange = changedValue => {
onChange && onChange(changedValue);
};
const [cycleOption, setCycleOption] = useState(type ?? num);
const [cycleInputValue, setCycleInputValue] = useState(
(typeof +num === 'number' && !_.isNaN(+num) && num) || '',
);
const onCycleChange = cycleOption => {
setCycleOption(cycleOption);
triggerChange(
cycleOption === 'always'
? cycleOption
: `${cycleInputValue} ${cycleOption}`,
);
};
const onInputChange = e => {
const newNumber = parseInt(e.target.value || '0', 10);
if (_.isNaN(newNumber)) {
return;
}
setCycleInputValue(newNumber);
triggerChange(
cycleOption === 'always' ? cycleOption : `${newNumber} ${cycleOption}`,
);
};
return (
<Input.Group>
{cycleOption === 'always' ? (
<Select
onChange={onCycleChange }
defaultValue={cycleOption}
style={{ width: 240 }}
>
{cycleOptions.map(item => (
<Select.Option key={item.value} value={item.value}>
{item.label}
</Select.Option>
))}
</Select>
) : (
<span>
<Input
value={cycleInputValue}
style={{ width: 156 }}
onChange={onInputChange}
/>
<Select
onChange={onCycleChange}
defaultValue={cycleOption}
style={{ width: 84 }}
>
{cycleOptions.map(item => (
<Select.Option key={item.value} value={item.value}>
{item.label}
</Select.Option>
))}
</Select>
</span>
)}
</Input.Group>
);
};
可以看出,自定义部分逻辑也比较简单,通过value参数可以展示组件的值,通过onChange方法,可以改变表单域的值,实现了表单带给我们的便捷功能。
总结
到此结束啦,快试试吧,实践出真知呀!
以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。