需求效果功能如图:
代码:
const [relateList, setRelateList] = useState(getPath(initInfo, 'relateLinkList', [] || []));
这个是因为此页面还实现了编辑功能,需要渲染初始数据,此时需要监听一个已有的列表,进行删除时候的删除处理,否则初始化后删除会出问题
<Form.Item label="关联链接" vvspan={24} labelCol={{ md: 3 }} wrapperCol={{ md: 21 }} style={{ marginBottom: '0' }} > <span style={{ color: urlList.length < 20 ? '#FFA22D' : 'rgba(0, 0, 0, 0.25)', cursor: 'pointer' }} onClick={add} > 添加链接 </span> </Form.Item> <Col vvspan={24} offset={3} className={styles.relateLinkList}> {urlList.map(k => ( <Form.Item vvspan={24} labelCol={{ md: 3 }} wrapperCol={{ md: 21 }}> <div key={k} style={{ position: 'relative' }}> {getFieldDecorator(`relateLinkList[${k}]`, { validateTrigger: ['onBlur'], rules: [ { min: 1, max: 2000, message: '不可超过2000字' }, { pattern: /^(ht|f)tps?:\/\/(([a-zA-Z0-9_-])+(\.)?)*(:\d+)?(\/((\.)?(\?)?=?&?([a-zA-Z0-9_-]|#|%)(\?)?)*)*$/i, message: '请输入有效网址!' } ],
initialValue: getPath(relateList, `${k - 1}`, '')
})( <Input style={{ width: '100%', paddingRight: '30px', boxSizing: 'border-box' }} /> )} <i className="icon iconfont iconshanchu" style={{ position: 'absolute', right: '12px', top: '0', cursor: 'pointer', color: '#333333' }} onClick={() => remove(k)} /> </div> </Form.Item> ))} </Col>
备注:这里遍历重新添加form.item的原因是加了网址校验,如果只遍历getFieldDecorator,校验只校验第一条数据
方法:
const remove = k => { setUrlList(urlList.filter(key => key !== k)); setRelateList(relateList.filter((_, index) => index !== k - 1)); }; const add = () => { const keys = form.getFieldValue('relateLinkList'); if (urlList.length < 20) { if ((keys && keys[keys.length - 1]) || keys === undefined) { const nextKeys = urlList.concat(urlList.length + 1); setUrlList(nextKeys); } } else { message.error('上限20个'); } };
数据提交的校验:
const newLinkList = values.relateLinkList && values.relateLinkList.filter(item => !!item); const params = { ...values, id: 0, relateLinkList: newLinkList, }; const result = await launchTask(params); if (result.code === 10000) { message.success(result.msg); router.goBack(); pageTabUtil.closeTab('/work/task/addTask'); } else { message.error(result.msg); }
-------------------------------------接上文警告线-----------------------------------------------上面的实现,发生了一个意想不到的问题:如果有初始值,那么增删都会出现bug----------------------------
因此重新封装了组件:
????表单项代码:
<Form.Item label="关联链接" vvspan={24} labelCol={{ md: 3 }} wrapperCol={{ md: 21 }} style={{ marginBottom: '0' }} > {getFieldDecorator('relateLinkList', { validateTrigger: ['onChange', 'onBlur'], rules: [ { validator(_, value, callback) { value.some(item => { const errorMessage = linkRef.current.getValidateError(item.link); if (errorMessage) { callback(errorMessage); return true; } return false; }); callback(); } } ], initialValue: relativeLinkData })(<RelativeLink childRef={linkRef} />)} </Form.Item>
拿到的数据初始化:
const relativeLinkData = (initInfo.relateLinkList || []).map((item, index) => ({ key: index + 1, link: item }));
封装的组件:
import React from 'react'; import { message, Input } from 'antd'; import cn from 'classnames'; import styles from './RelativeLink.less'; let id; const validators = [ { validator: link => link && link.length <= 2000, message: '不可超过2000字' }, { validator: link => /^(ht|f)tps?:\/\/(([a-zA-Z0-9_-])+(\.)?)*(:\d+)?(\/((\.)?(\?)?=?&?([a-zA-Z0-9_-]|#|%|\)|\()(\?)?)*)*$/i.test( link ), message: '请输入有效网址' } ]; const getValidateError = link => { let msg; if (!link) { return msg; } validators.some(item => { if (!item.validator(link)) { msg = item.message; return true; } return false; }); return msg; }; const RelativeLink = ({ value, onChange, childRef }) => { const handleAdd = () => { if (value && value.length < 20) { if (!id) id = value.length || 0; id += 1; onChange([...value, { key: id }]); } else { message.error('上限20个'); } }; const handleOnChange = (e, key) => { onChange( value.map(it => { if (it.key === key) { return { ...it, link: e.target.value }; } return it; }) ); }; const handleDelete = key => { onChange(value.filter(it => it.key !== key)); }; React.useImperativeHandle(childRef, () => ({ getValidateError })); return ( <> <span style={{ color: value.length < 20 ? '#FFA22D' : 'rgba(0, 0, 0, 0.25)', cursor: 'pointer' }} onClick={handleAdd} > 添加链接 </span> {value.map(item => ( <div key={item.key} style={{ position: 'relative' }} className={cn({ [styles.success]: !getValidateError(item.link) })} > <Input style={{ width: '100%', paddingRight: '30px', boxSizing: 'border-box' }} value={item.link} onChange={e => handleOnChange(e, item.key)} /> <i className="icon iconfont iconshanchu" style={{ position: 'absolute', right: '12px', top: '0', cursor: 'pointer', color: '#333333' }} onClick={() => handleDelete(item.key)} /> </div> ))} </> ); }; export default RelativeLink;
引入页面:
import { RelativeLink } from './components';
const linkRef = React.useRef();
提交时候的数据处理为网址数组:
const newLinkList = values.relateLinkList && values.relateLinkList.filter(item => item.link && item.link.length > 0).map(item => item.link);
实际效果【当添加后没输入则不校验,否则必须小于2000字且要求网址格式正确】
拿到的初始数据:
渲染效果:
随意增删,也不会出现渲染问题
查看更多关于react+antd form表单项动态添加数据的实现的详细内容...
声明:本文来自网络,不代表【好得很程序员自学网】立场,转载请注明出处:http://haodehen.cn/did222495