AntdFormBattle
v3
缘起
1 亿年没写缘起了,但这次我一定要写。Form 表单,让我的眼泪流尽了。数据交互方面,和 React 的 Hooks 还是有点小坑的,尤其是初始化的时候。昨天碰到的问题是,初始化之后,输入框无法输入,选择框无法选择,解决方法是降低层级,FormItem
就直接用列表渲染出来。
今天碰到的问题是,动态渲染的列表无法对应上,对象转数组的方法报错。这里我是扩充了数组长度,来满足对象的生成,最后再清除列表中的空对象。
还有一点,就是rule
校验对象,这个明天再补充。
2022-11-10 已补充
validator
的使用
1. 数据初始化
Form
表单中有几个FormItem
,传入的数据就要给什么,这是最容易明白的数据交互。
useEffect(() => {
const { bankAccountInfo, ...others } = enterpriseSet;
if (bankAccountInfo) {
// 数组转为对象值
const afterFormatInfo = bankAccountInfo.reduce((prev, cur, index) => {
prev[`bank${index}`] = cur.bankEnglishShort;
prev[`bankAccount${index}`] = cur.bankAccountNumber;
return prev;
}, {});
const temp = { ...others, ...afterFormatInfo };
setFieldsValue(temp);
} else {
const temp = { ...others };
setFieldsValue(temp);
}
}, [enterpriseSet, setFieldsValue]);
这里重要的就是setFieldsValue
方法,把传入值和Form
相对接。
2. 数据交互
如果第一步做好,其实这一步就不用管了,antd 会自己连接好。如果没搭上,那我的建议是,回到第一步初始化的时候,看看哪里出了毛病。
3. 表单项的增减
增加
push 返回的是新字符串长度!!!
const addBank = useCallback(() => {
const newList = bankList;
const lastNumber = newList[newList.length - 1] + 1;
const newBankList = [...bankList, lastNumber];
setBankList(newBankList);
}, [bankList]);
减少
减少比增加难顶,需要注意其中缺失的数组边界情况,在对象转数组这一步需要考虑到
<Form.Item label="银行">
{getFieldDecorator(`bank${index}`, {
rules: [
{
required: true,
message: "请选择银行",
},
],
})(
<Select placeholder="请选择">
{bankListForUpdate.map(({ bankEnglishShort, bankName }) => {
return <Option value={bankEnglishShort}>{bankName}</Option>;
})}
</Select>
)}
{bankList.length > 1 ? (
<Popconfirm
title="你确定要删除这个账号吗?"
onConfirm={() => removeBank(index)}
onCancel={() => {}}
okText="确定"
cancelText="取消"
>
<Icon className={styles["dynamic-delete-button"]} type="delete" />
</Popconfirm>
) : null}
</Form.Item>
const removeBank = useCallback(
(k) => {
const newBankList = bankList.filter((key) => key !== k);
setBankList(newBankList);
},
[bankList]
);
4. 表单数据校验
表单组件中直接调getFieldsValue
函数
const { getFieldsValue } = form;
父组件中,使用ref
,绑在表单组件上
<Entry ref={formRef} bankListForUpdate={bankListForUpdate} />
const formRef = useRef();
// ...
formRef.current.validateFieldsAndScroll((err, values) => {
console.log(values);
if (err) {
console.log("校验不通过");
return;
}
5. 数据转换
bank0 bankAccount0 <-> [{bank: ..., bankAccount: ...}]
列表转对象
// 数组转为对象值
const afterFormatInfo = bankAccountInfo.reduce((prev, cur, index) => {
prev[`bank${index}`] = cur.bankEnglishShort;
prev[`bankAccount${index}`] = cur.bankAccountNumber;
return prev;
}, {});
const temp = { ...others, ...afterFormatInfo };
对象转列表
export function getFormatArray(values) {
return Object.keys(values)
.reduce((prev, cur) => {
const isAccount = cur.includes("bankAccount");
const isBank = cur.includes("bank");
const isKeyWord = isAccount || isBank;
if (isKeyWord) {
const curIndex = cur.replace(/bankAccount|bank/, "");
// 根据curIndex大小加数组
// cur = 3, prev中就需要为[{},{},{},{}] prev[3]要有值
while (prev.length - 1 < curIndex) {
prev.push({});
}
// 注意顺序,isAccount必定在isBank前
if (isAccount) {
prev[parseInt(curIndex)].bankAccountNumber = values[cur];
} else {
prev[parseInt(curIndex)].bankEnglishShort = values[cur];
}
}
return prev;
}, [])
.filter((key) => Object.keys(key).length);
}
// 将对象转换为数组值,注意最后空数组的处理
const deliver = getFormatArray(values).map((value) => {
value.bankName = bankListForUpdate.find(
(key) => key.bankEnglishShort === value.bankEnglishShort
).bankName;
return value;
});
6. validator 的使用
表单项中插入校验方法
<Form.Item label="APPKey">
{getFieldDecorator("appKey", {
rules: [
{
required: true,
validator: validattorForAppKey,
},
],
})(<Input allowClear placeholder="请输入" />)}
</Form.Item>
校验方法
回调cb
返回的是message
校验出错提示内容,如为空,则不显示提示。
// AppKey校验
const validattorForAppKey = (rule, value, cb) => {
const pattern = /[^A-Za-z0-9]/g;
const notCrossReg = !!pattern.test(value);
const isSuitableLength = value.length === 8 || value.length === 16;
if (notCrossReg || !isSuitableLength) {
cb("请输入8位或者16位AppKey");
} else {
cb();
}
};
如果在FormItem
中先写了message
内容,则会优先使用
{
required: true,
validator: lengthValidator,
// pattern: /[A-Za-z0-9-]/,
message: "请输入9-35位的银行账号",
},
// 长度校验
const lengthValidator = (rule, value, cb) => {
try {
const pattern = /[^A-Za-z0-9-]/g;
const notCrossReg = !!pattern.test(value);
const isSuitableLength = value.length >= 9 && value.length <= 35;
if (notCrossReg || !isSuitableLength) {
throw new Error("wrong");
}
cb();
} catch (err) {
cb(err);
}
};
正则校验
如value
非数字字母大小写与-,则notCrossReg
则为 true
const pattern = /[^A-Za-z0-9-]/g;
const notCrossReg = !!pattern.test(value);
7. 动态增减表单项 bug
如果采用之前的bankList
作为表单项增减的标杆,初始化的时候会有新的问题。所以还得采用 antd 自己的表单数据交互方法
这里我使用的时候死循环了,但之前看相关问题,似乎也有解决方法
动态增加 form 表单项中 我尝试用 useState 创建了一个 BankList,但是后续遇到很多问题,比如初始化个数对不上。遂选择用 form.getFieldValue('bankList')来创建动态表单长度的数组,但现在遇到的问题是 setFieldValue 设置了 bankList 新的数值,在回显的时候,这个 bankList 的值都没了
我在想是用哪种来初始化 动态表单的项,是用 useState,还是 antd 官网的 getFieldValue,第一种我已经踩了坑,第二种正在踩坑
初始化的时候,bankList 的值就没有附上去
- 删除后不保存 再次点击 不会存在
- 保存后再次点击是新的值
表单数据的获取接口,应该在渲染表单的时候,获取到最新的!!!我用 redux 是给自己挖坑了!
表格无数据时更换背景图,不用ConfigProvider
这么麻烦
如何修改 antd 表格默认的暂无数据图标 - 简书 (jianshu.com)
附录
二次封装 Antd 中的 Form,根据配置项生成表单 - 掘金 (juejin.cn)
v4
v3->v4 官网有参考文档
指路:https://4x-ant-design.antgroup.com/components/form/v3-cn/