Skip to content
Have a brief look

Ant Design 用法与坑点总结

Ant Design

Ant Design 国内高速镜像

前言

Ant Design 是蚂蚁出品的出色优秀的 React 组件库,相信使用 React 进行管理系统开发的小伙伴们或多或少都接触过 Ant Design。很多公司基于 React 开发的管理端系统也都是使用 Ant Design 的组件库。

因此,了解 Ant Design 的常见用法与坑点还是有必要的。

本系列文章针对 Ant Design 一些官方文档虽有提及,但是容易被忽略的,又比较重要常见的用法进行总结。同时对工作使用过程当中遇到的坑点进行归纳。

TIP

文章使用的 4.x 版本的 Ant Design。

Form 触发表单验证

Form.Itemrules 表单验证规则,需要调用 validateFields() 方法触发表单验证才能真正阻止不符合规则的表单的提交。

如果仅仅定义了 rules 规则,而没有调用 validateFields() 方法进行验证,即使表单没通过校验,依然可以提交。

jsx
const handleSubmit = async () => {
  // 触发表单验证,注意该方法是异步的
  await form.validateFields()
  submitForm()
}
jsx
<Form.Item
  name="mobile"
  label="手机号码或座机"
  rules={[
    { required: true, message: '请输入手机号码或座机号码' },
    { pattern: /......../, message: '请输入正确的手机号或座机号' },
  ]}
>
  <Input placeholder="请输入手机号或座机号" />
</Form.Item>

Form 自定义校验规则的两种写法

自定义表单校验规则灵活度更高,可以针对一些特殊场景、特殊规则进行设置。日常工作使用概率比较大。

方式一:对象写法

jsx
<Form.Item
  name="mobile"
  label="手机号"
  rules={[
    {
      // rule 参数是 Form.Item 的校验规则 rules
      validator: (rule, value) => {
        if (!value) {
          return Promise.reject(new Error('请输入手机号'))
        }
        if (value.length > 11) {
          return Promise.reject(new Error('手机号不超过11位'))
        }
        return Promise.resolve()
      },
    },
  ]}
>
  <Input placeholder="请输入手机号" />
</Form.Item>

itemProps 具体内容:

itemProps 具体内容

方式二:函数写法

jsx
<Form.Item
  name="password"
  label="账户密码"
  rules={[
    ({ getFieldValue }) => ({
      validator(_, value) {
        if (!value) {
          return Promise.reject(new Error('请输入账户密码'))
        }
        if (value === getFieldValue('password')) {
          console.log('getFieldValue() 方法获取对应字段名的值')
        }
        return Promise.resolve()
      },
    }),
  ]}
>
  <Input placeholder="请输入你的账户密码" />
</Form.Item>

方式二可以取到 Form 表单实例的所有方法:

Form实例方法

相比而言,方式二的自由度更高,功能也相对更强大,因为提供了 getFieldValue() 等方法使用。

但个人认为,方式一的写法更加优雅,并且一般情况下,方式一也足够使用了,可以解决日常工作大部分自定义表单验证规则的场景。

getFieldValue

Select 下拉框设置初始值

在开发工作中,需要为 Form 表单中的 Select 下拉框设置初始值的情况也屡见不鲜。下面介绍两种常见的解决方式。

使用 setFieldsValue()

该方法通过利用 Form 表单实例提供的 setFieldsValue() 方法设置初始值,在页面初次展示即可调用该方法设置初始值。

jsx
const showModal = () => {
  form.setFieldsValue({
    color: "red",
    ...
  });
  ...
}
jsx
<Form.Item name="color" label="车辆外表颜色" rules={[{ required: true, message: '请选择车辆外表颜色' }]}>
  <Select placeholder="请选择车辆外表颜色" {...props}>
    <Option key={1} value="red">
      红色
    </Option>
    <Option key={2} value="yellow">
      黄色
    </Option>
    <Option key={3} value="blue">
      蓝色
    </Option>
  </Select>
</Form.Item>

使用 initialValues 属性

通过使用 Form 表单的 initialValues 属性,也能够为表单域设置初始值。

jsx
const initialValues = { color: 'yellow' }

<Form form={form} initialValues={initialValues}>
  <Form.Item name="color" label="车辆外表颜色" rules={[{ required: true, message: '请选择车辆外表颜色' }]}>
    <Select placeholder="请选择车辆外表颜色" {...props}>
      <Option key={1} value="red">
        红色
      </Option>
      <Option key={2} value="yellow">
        黄色
      </Option>
      <Option key={3} value="blue">
        蓝色
      </Option>
    </Select>
  </Form.Item>
</Form>

使用 initialValue 属性

通过使用 Form.IteminitialValue 属性,单独为该表单项设置初始值,不影响其他表单项。

jsx
<Form.Item name="color" label="车辆外表颜色" rules={[{ required: true, message: '请选择车辆外表颜色' }]} initialValue="blue">
  <Select placeholder="请选择车辆外表颜色" {...props}>
    <Option key={1} value="red">
      红色
    </Option>
    <Option key={2} value="yellow">
      黄色
    </Option>
    <Option key={3} value="blue">
      蓝色
    </Option>
  </Select>
</Form.Item>

错误:使用 defaultValue 属性

当时遇到这个问题时,我第一个想法是利用 Select 下拉框的 defaultValue 属性,来设置默认值,如下代码:

jsx
<Form.Item name="color" label="车辆外表颜色">
  <Select defaultValue="red">
    <Option key={1} value="red">
      红色
    </Option>
    <Option key={2} value="yellow">
      黄色
    </Option>
    <Option key={3} value="blue">
      蓝色
    </Option>
  </Select>
</Form.Item>

但是该方法并不生效,这是因为 Form.Item 设置了 name 属性,Ant Design 增加了限制,导致其不生效。

TIP

设置了  name  属性的  Form.Item  包裹的表单控件,不能用控件的  value  或  defaultValue  来设置表单域的值,默认值可以用  Form  的  initialValues  来设置

官网文档相关说明

defaultValue不可用

initialValues的区别

相关链接:

Form 表单

Input 输入框内容去除头尾空白

在填写 Form 表单时,我们往往会遇到这样的需求:Input 输入框输入的内容头尾不允许带有空格。有点类似于 trim() 方法的功能。

针对这个功能,整体上有 3 中实现思路:

  • 在输入的过程中,禁止输入空白符
  • 利用表单验证,如果头尾存在空格,则提示用户
  • 提交表单时,自动去除头尾空白,用户无感

禁止输入空格

该方式利用 Form.ItemgetValueFromEvent 属性,这个属性可以设置如何把输入的内容转换为表单项实际获取的值。需要传入一个函数。

js
<Form.Item
  name="city"
  label="城市"
  getValueFromEvent={(e) => e.target.value.replace(/(^\s*)|(\s*$)/g, '')}
  // getValueFromEvent={(e) => { console.log(e.target); return e.target.value; }}
>
  <Input />
</Form.Item>

在上述代码中,e.target.value 即为输入框输入的值,而 Form.Item 表单项实际获取到的值是 e.target.value.replace(/(^\s*)|(\s*$)/g, '')

这种方法在页面交互上表现为用户无法输入空格,其实用户体验并不友好,让人感觉更像是这个输入框有 bug,竟然不让我输入空格。

利用表单验证

使用上面提到的表单验证方法。

js
<Form.Item name="city" label="城市" rules={[{ pattern: /(^\S)((.)*\S)?(\S*$)/, message: '前后不能有空格' }]}>
  <Input />
</Form.Item>
js
<Form.Item
  name="city"
  label="城市"
  rules={[
    {
      validator: (_, value) => {
        const reg = /(^\s+)|(\s+$)/
        if (reg.test(value)) {
          return Promise.reject(new Error('前后不能有空格'))
        }
        return Promise.resolve()
      },
    },
  ]}
>
  <Input />
</Form.Item>

这里就体现出了自定义表单验证的优势,写法灵活,且在某些场景下能降低思考难度。/(^\s+)|(\s+$)//(^\S)((.)*\S)?(\S*$)/ 这两个正则,显然前者更容易写出来,后面这个不查半天资料都难写。

提交表单调用 trim() 方法

这种方法就是随便用户输入,在提交表单的时候再使用 trim() 方法去除前后空格。

但是既然设置了这个规则,还是有必要让用户知道,给用户提示,因此综合下来第二种方法是比较推荐的解决办法。

Form.Item 如何包裹多个标签内容

问题背景

开发工作中遇到一个需求:需要在开关表单项后面增加一句文本内容,如下图所示。

开关后增加文本内容

错误方法

不能直接把文本内容直接放在 Form.Item 里面,否则后面提交表单时 form.getFieldsValue() 无法正确获取开关的值。

jsx
// 始终只能取到初始值
<Form.Item label="开关" name="isOpen" initialValue={false} valuePropName="checked">
  <Switch />
  <span>这是文本内容</span>
</Form.Item>

后面又尝试把文本内容放在 Form.Item 后面,但样式始终存在问题,Form.Item 如果增加自定义样式,其样式会乱,比如表单标签被开关挡住了,总之也不行。

解决方案

经过高人支招,了解了Form.Item 表单项如果需要在表单后面增加额外的标签组件,需将整体封装为一个大的组件写在 FormItem 内,否则 form 表单无法获取到正确的表单值。

js
const SwitchCom = (props) => {
  const { value, type } = props

  const handleChangeSwitch = (val) => {
    form.setFieldsValue({ isOpen: val })
  }

  return (
    <div style={{ display: 'flex', alignItems: 'center' }}>
      <Switch checked={value} onChange={(val) => handleChangeSwitch(val)} />
      <span style={{ color: '#bfbfbf', marginLeft: '10px' }}>这是文本内容</span>
    </div>
  )
}
jsx
<Form.Item label="开关" name="isOpen" initialValue={false}>
  <SwitchCom type="good" />
</Form.Item>

注意事项

1、需要给 SwitchonChange 事件绑定函数,开关改变时手动修改表单的 isOpen 值。

2、props 里有表单的 value 值,若 Form.Item 设置了 valuePropName 就是对应的字符串,也可以传入自定义参数。

Table 合并单元格

Table 合并单元格的写法,官网上的写法是错的.

js
render: (text, record, index) => {
  return {
    children: text,
    props: {
      // 合并单元格
      rowSpan: [0, 5].includes(index) ? 5 : 0,
    },
  };
}

下拉框选择多个显示省略号

设置 maxTagCount="responsive,根据屏幕大小自适应展示多少个 tag。

jsx
<Select mode="multiple" maxTagCount="responsive">
  <Option value="1">Option 1</Option>
  <Option value="1">Option 1</Option>
</Select>

maxTagCount="responsive"

Input 输入框使用 defaultValue 内容无法改变

在遍历渲染时,多个 Input 输入框使用 defaultValue,只会记录第一个 Input 的初始值,后面的都和第一个一样。

解决方法:给 Input 输入框增加 key 属性。

jsx
const columns = [
  {
    title: '名称',
    dataIndex: 'name',
    render: (text, record) => {
      return (
        <Input key={nanoid()} defaultValue={text} />
        <Input key={String(Date.now())} defaultValue={text} />
        // 这个不行,不知道为何
        <Input key={new Date().toTimeString()} defaultValue={text} />
      );
    },
  },
]

react input的defaultValue不会变化

If there is any reprint or CV, please mark the original address of this website