Ant Design 中 FormList 的使用
有时候在我们的表单中可能存在动态增删的情况,比如需要登记报名的人员,人员的数量是不确定的,每个人员都要登记姓名和身份证号,这便是动态表单。
简单的动态表单
import { FC, Fragment } from "react"
import { Button, Form, Input } from "antd"
import FormItem from "antd/es/form/FormItem"
import FormList from "antd/es/form/FormList"
const App: FC = () => (
<Form onFinish={console.dir}>
<FormList name="hobbies" initialValue={["钓鱼"]}>
{(fields, { add }) => (
<Fragment>
{fields.map(({ key, name }) => (
<FormItem key={key} name={name}>
<Input />
</FormItem>
))}
<Button onClick={() => add()}>Add</Button>
</Fragment>
)}
</FormList>
<Button htmlType="submit">Submit</Button>
</Form>
)
export default App
在这个表单中,hobbies 字段是动态的,使用起来也非常简单,但是有以下注意点:
-
FormList不接受泛型参数,直接给它传递动态的字段名即可,这里是hobbies -
FormList的initialValue就是初始值,如果,你希望初始时就有一个输入框呈现,可以传入[undefined] -
initialValue的每一项都会传递给FormItem,继而传递给内部的表单元素。所以,必须传递相应类型的值,比如Input组件可以接受的初始值是string | undefined,那initialValue可以接受的类型就是(string | undefined)[],而RangePicker可以接受的初始值是[Dayjs | null, Dayjs | null] | undefined,那initialValue可以接受的类型就是([Dayjs | null, Dayjs | null] | undefined)[] -
FormList接受一个函数作为children。函数的有三个参数:fields、operation和meta -
fields是一个由field组成的数组,用于渲染动态表单的每一个子元素。 -
field有两个属性key和name,都是用于直接传递给FormItem。注意不能直接使用{...field}的形式。因为React组件的key属性必须显式地声明,也就是必须是key={key}的形式。 -
opertion有三个属性add、remove和move属性。 -
add是一个用于添加表单数量的方法,接受两个参数defaultValue和insertIndex。defaultValue会传递给新增的FormItem。与第 3 点类似,初始值的类型必须匹配。insertIndex用于设置插入的位置。不能使用<Button onClick={add}>Add</Button>的形式,因为onClick是传递参数MouseEvent的,只能使用<Button onClick={() => add()}>Add</Button> -
remove是一个用于删除某个表单的方法,接受一个参数index,可以是number也可以是number[],对应field中的name -
move是一个用于移动表单的方法,接受两个参数from和to,都对应field中的name
嵌套的动态表单
import { FC, Fragment } from "react"
import { Button, Form, Input } from "antd"
import FormItem from "antd/es/form/FormItem"
import FormList from "antd/es/form/FormList"
const App: FC = () => (
<Form onFinish={console.dir}>
<FormList name="persons" initialValue={[{ name: "Tom", age: "18" }]}>
{(fields, { add }) => (
<Fragment>
{fields.map(({ key, name }) => (
<Fragment key={key}>
<FormItem name={[name, "name"]} label="姓名">
<Input />
</FormItem>
<FormItem name={[name, "age"]} label="年龄">
<Input />
</FormItem>
</Fragment>
))}
<Button onClick={() => add()}>Add</Button>
</Fragment>
)}
</FormList>
<Button htmlType="submit">Submit</Button>
</Form>
)
export default App
有时候,我们动态的表单可能不止一项属性,可能存在多个属性,属性中可能还有多个属性,这时便是嵌套的动态表单,这与简单的动态表单有以下几点不同:
-
name属性无法直接使用,必须是使用[name, "age"]这种路径数组的形式 -
initialValue也不再是valueType[]的形式,而是{ name: valueType }[]的形式 -
FormList也可以再次嵌套FormList:<FormList name="persons">
{fields =>
fields.map(({ name }) => (
<FormItem label="爱好">
<FormList name={[name, "hobbies"]} initialValue={["钓鱼"]}>
{(fields, { add }) => (
<Fragment>
{fields.map(({ key, name }) => (
<FormItem key={key} name={name}>
<Input />
</FormItem>
))}
<Button onClick={() => add()}>Add</Button>
</Fragment>
)}
</FormList>
</FormItem>
))
}
</FormList>