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 自己的表单数据交互方法

这里我使用的时候死循环了,但之前看相关问题,似乎也有解决方法

img

动态增加 form 表单项中 我尝试用 useState 创建了一个 BankList,但是后续遇到很多问题,比如初始化个数对不上。遂选择用 form.getFieldValue('bankList')来创建动态表单长度的数组,但现在遇到的问题是 setFieldValue 设置了 bankList 新的数值,在回显的时候,这个 bankList 的值都没了

我在想是用哪种来初始化 动态表单的项,是用 useState,还是 antd 官网的 getFieldValue,第一种我已经踩了坑,第二种正在踩坑

初始化的时候,bankList 的值就没有附上去

1669022453607

  1. 删除后不保存 再次点击 不会存在
  2. 保存后再次点击是新的值

表单数据的获取接口,应该在渲染表单的时候,获取到最新的!!!我用 redux 是给自己挖坑了!

表格无数据时更换背景图,不用ConfigProvider这么麻烦

如何修改 antd 表格默认的暂无数据图标 - 简书 (jianshu.com)

附录

二次封装 Antd 中的 Form,根据配置项生成表单 - 掘金 (juejin.cn)

v4

v3->v4 官网有参考文档
指路:https://4x-ant-design.antgroup.com/components/form/v3-cn/

posted @ 2022-11-24 21:17  乐盘游  阅读(14)  评论(0编辑  收藏  举报