React Antd Form.List使用教程

1,580 阅读3分钟

React Antd Form.List使用教程

Form.List 是 Ant Design (antd) 中用于动态管理表单字段的组件,特别适合处理数组类型的表单数据。它的核心是通过函数式渲染(render props)的方式,将字段列表和操作方法暴露给开发者。以下是 Form.List 组件中各个参数的详细解释。

Form.List 的基本结构

<Form.List name="字段名">
  {(fields, operations, meta) => ReactNode}
</Form.List>
  • name:字段名,用于标识表单数据中的数组字段。
  • children:一个函数,接收三个参数:fieldsoperations 和 meta,返回 React 节点。

1. fields 参数

fields 是一个数组,包含了当前 Form.List 中所有的动态字段。每个字段是一个对象,包含以下属性:

字段对象的属性

  • keystring

    • 每个字段的唯一标识符,用于 React 的 key 属性。
  • namenumber

    • 字段在数组中的索引(从 0 开始)。
  • fieldKeynumber

    • 与 name 相同,用于兼容旧版本。
  • isListFieldboolean

    • 标识该字段是否属于 Form.List,通常为 true
fields.map(({ key, name, fieldKey, ...restField }) => (
  <Form.Item key={key} name={[name, 'fieldName']}>
    <Input />
  </Form.Item>
));

2. operations 参数

operations 是一个对象,包含用于操作字段的方法:

方法:

  • add(defaultValue?: any, insertIndex?: number) => void

    • 添加一个新字段。

    • 参数

      • defaultValue:可选,新字段的默认值。
      • insertIndex:可选,插入字段的位置(从 0 开始)。如果不传,默认添加到末尾。
    add({ fieldName: '默认值' }, 0); // 在索引 0 处插入一个新字段
    
  • move(from: number, to: number) => void

    • 将一个字段从 from 索引移动到 to 索引。
    move(0, 1); // 将索引 0 的字段移动到索引 1
    
  • remove(index: number | number[]) => void

    • 删除一个或多个字段。

    • 参数

      • index:要删除的字段的索引(从 0 开始),可以是一个索引或索引数组。
    remove(0); // 删除索引为 0 的字段
    remove([0, 1]); // 删除索引为 0 和 1 的字段
    

3. meta 参数

meta 是一个对象,包含当前 Form.List 的元信息:

属性:

  • errorsReactNode[]

    • 当前 Form.List 中所有字段的验证错误信息。
  • warningsReactNode[]

    • 当前 Form.List 中所有字段的验证警告信息。
{meta.errors.length > 0 && (
  <div style={{ color: 'red' }}>{meta.errors.join(', ')}</div>
)}

4. name 属性

name 是 Form.List 的一个必需属性,用于标识表单数据中的数组字段。

<Form.List name="tasks">
  {(fields) => fields.map(({ key, name }) => (
    <Form.Item key={key} name={[name, 'taskName']}>
      <Input />
    </Form.Item>
  ))}
</Form.List>
  • 在这个示例中,name="tasks" 表示表单数据中的 tasks 字段是一个数组。
  • 每个字段的 name 属性需要设置为 [name, 'fieldName'],其中 name 是字段的索引,fieldName 是字段的名称。

5. 完整示例

以下是一个完整的示例,展示了 Form.List 的用法:

import { Form, Input, Button, Space } from 'antd';

const NestedFormList = () => {
	const onFinish = (values) => {
		console.log('Received values of form:', values);
	};

	return (
		<Form onFinish={onFinish}>
			{/* 外层:项目列表 */}
			<Form.List name="projects">
				{(projects, { add: addProject, remove: removeProject }) => (
					<>
						{projects.map(({ key: projectKey, name: projectName }) => (
							<div key={projectKey} style={{ marginBottom: 16, border: '1px solid #ddd', padding: 16 }}>
								<Form.Item
									{...projects[projectName]}
									name={[projectName, 'projectName']}
									label="项目名称"
									rules={[{ required: true, message: '请输入项目名称' }]}
								>
									<Input placeholder="项目名称" />
								</Form.Item>

								{/* 中层:任务列表 */}
								<Form.List name={[projectName, 'tasks']}>
									{(tasks, { add: addTask, remove: removeTask }) => (
										<>
											{tasks.map(({ key: taskKey, name: taskName }) => (
												<div key={taskKey} style={{ marginBottom: 16, padding: 8, border: '1px solid #eee' }}>
													<Form.Item
														{...tasks[taskName]}
														name={[taskName, 'taskName']}
														label="任务名称"
														rules={[{ required: true, message: '请输入任务名称' }]}
													>
														<Input placeholder="任务名称" />
													</Form.Item>

													{/* 内层:子任务列表 */}
													<Form.List name={[taskName, 'subTasks']}>
														{(subTasks, { add: addSubTask, remove: removeSubTask }) => (
															<>
																{subTasks.map(({ key: subTaskKey, name: subTaskName }) => (
																	<div key={subTaskKey} style={{ marginBottom: 8 }}>
																		<Form.Item
																			{...subTasks[subTaskName]}
																			name={[subTaskName, 'subTaskName']}
																			label="子任务名称"
																			rules={[{ required: true, message: '请输入子任务名称' }]}
																		>
																			<Input placeholder="子任务名称" />
																		</Form.Item>
																		<Button type="link" onClick={() => removeSubTask(subTaskName)}>
																			删除子任务
																		</Button>
																	</div>
																))}
																<Button type="dashed" onClick={() => addSubTask()} block>
																	添加子任务
																</Button>
															</>
														)}
													</Form.List>

													<Button type="link" onClick={() => removeTask(taskName)}>
														删除任务
													</Button>
												</div>
											))}
											<Button type="dashed" onClick={() => addTask()} block>
												添加任务
											</Button>
										</>
									)}
								</Form.List>

								<Button type="link" onClick={() => removeProject(projectName)}>
									删除项目
								</Button>
							</div>
						))}
						<Button type="dashed" onClick={() => addProject()} block>
							添加项目
						</Button>
					</>
				)}
			</Form.List>

			<Form.Item>
				<Button type="primary" htmlType="submit">
					提交
				</Button>
			</Form.Item>
		</Form>
	);
};

export default NestedFormList;

image.png

image.png

注意事项

  • name 路径:在嵌套的 Form.List 中,name 属性需要正确设置路径(如 [name, 'fieldName'])。
  • 字段索引fields 中的 name 是字段的索引(从 0 开始),用于绑定表单数据。
  • 动态操作add 和 remove 方法用于动态添加或删除字段,move 用于调整字段顺序