前言
项目中需要实现一个选中节点修改其连接桩信息的功能,考虑后选择Table
组件实现,即可以展示数据,antd
官网也有如何实现可编辑行的示例,虽然看起来不复杂,但刚开始写的时候对于Table
行以及Form.Item
的切换以及数据流通不是特别清晰,下文主要整理如何用Ant Design
实现可以编辑的Table
组件,以及梳理清楚实现改功能的一些细节
功能实现
主要组件
这里主要用了antd
的Table
组件,下面尽量讲解各部分含义,不详尽处参见官方文档
这里主要关注form
、EditableCell
、data
、mergedColumns
form
,表单实例EditableCell
,由于要实现的功能是点击编辑按钮,表单变为可编辑的状态,所以该组件对应表单编辑时的状态data
,表单源数据mergedColumns
,表格列的配置描述
<Form form={form}>
<Table
components={{
body: {
cell: EditableCell,
},
}}
dataSource={data}
columns={mergedColumns}
/>
</Form>
数据数组
根据选择节点信息解析生成
这里的key
同样是每一行的key
,用作唯一标识,同时也是判断是否是当前行在编辑
//表格数据格式
interface Item {
key: string;
name: string;
dir: string;
type: string;
}
//表单数据
const [data, setData] = useState<Item[]>([]);
useEffect(() => {
if (selectNode === null) return;
const originData: Item[] = [];
//将ports数据转化为table原始数据
for (const port of selectNode.getPorts()) {
//...
originData.push({
key: port.id as string,
name: port.attrs?.text.text as string,
dir,
type,
});
}
setData(originData);
}, [selectNode]);
表格列的配置描述
由columns
生成,解析columns
中数据,供EditableCell
使用
const columns = [
{
title: '端口名称',
dataIndex: 'name',
width: '30%',
editable: true,
},
//...
{
title: '操作',
dataIndex: 'operation',
render: (_: any, record: Item) => {
const editable = isEditing(record);
return editable ? (...) : (...)
},
},
];
const mergedColumns = columns.map(col => {
if (!col.editable) {
return col;
}
return {
...col,
onCell: (record: Item) => ({
record,
dataType: col.dataIndex,
dataIndex: col.dataIndex,
title: col.title,
editing: isEditing(record),
}),
};
});
mergedColumns
中return
出对象中的onCell
,将数据传递给了table
中的cell
,这里的dataType
等字段均可以自定义,以支持cell
组件中的判断逻辑
(注意这里col.editable
为true
才会返回onCell
中的数据,即对于可编辑单元格才额外返回需要的数据)
components={{
body: {
cell: EditableCell,
},
}}
可编辑单元格
数据格式如下,来源于mergedColumns
//可编辑单元数据格式
interface EditableCellProps extends React.HTMLAttributes<HTMLElement> {
//是否正在编辑
editing: boolean;
//区分Form.Item 'name' | 'dir' | 'type'
dataIndex: string;
//Form字段标题
title: string;
//数据类型,用于区分编辑状态是input还是select等
dataType: string;
//记录正在编辑的数据
record: Item;
//索引
index: number;
children: React.ReactNode;
}
可编辑单元格组件如下,根据editing
变量选择展示Form.Item
还是Table
自身行数据
//可编辑单元格
const EditableCell: React.FC<EditableCellProps> = ({
editing,
dataIndex,
title,
dataType,
children,
...restProps
}) => {
const selectDir = <Select options={nodeType.current === 'func' ? funcDirOptions : moduleDirOptions} />;
const selectType = <Select options={typeOptions} />;
//编辑单元格
const editNode = () => {
switch (dataType) {
case 'name':
return <Input />;
case 'dir':
return selectDir;
case 'type':
return selectType;
}
};
return (
<td {...restProps}>
{editing ? (
<Form.Item name={dataIndex}>
{editNode()}
</Form.Item>
) : (
children
)}
</td>
);
};
这里分别采用了两种输入,<Input />
和<Select />
,关于组件中的value
是如何被获取到的,下面是Form.Item
的官方解释
被设置了
name
属性的Form.Item
包装的控件,表单控件会自动添加value
(或valuePropName
指定的其他属性)onChange
(或trigger
指定的其他属性),数据同步将被 Form 接管,这会导致以下结果:
- 你不再需要也不应该用
onChange
来做数据收集同步(你可以使用 Form 的onValuesChange
),但还是可以继续监听onChange
事件。- 你不能用控件的
value
或defaultValue
等属性来设置表单域的值,默认值可以用 Form 里的initialValues
来设置。注意initialValues
不能被setState
动态更新,你需要用setFieldsValue
来更新。- 你不应该用
setState
,可以使用form.setFieldsValue
来动态改变表单值。
获取table
中行信息
获取table
中行信息是通过render
函数获取,关于render
函数的api
说明如下:
生成复杂数据的渲染函数,参数分别为当前行的值,当前行数据,行索引
function(text, record, index) {}
由于这里只需要用到record
参数,所以_:any
标识占位,这样就可以正确获取到位置第二的record
参数
const columns = [
//...
{
title: '操作',
dataIndex: 'operation',
render: (_: any, record: Item) => {
const editable = isEditing(record);
return editable ? (
<Typography.Link onClick={() => save(record.key)}>
保存
</Typography.Link>
) : (
<Typography.Link disabled={editingKey !== ''} onClick={() => edit(record)}>
编辑
</Typography.Link>
);
},
},
]
数据更新流程
如上面所说,我们需要调用setFieldsValue
才能改变表单数据,所以我们的行在切换成表单状态时需要根据该行信息预填表单,即调用如下edit
函数;当编辑完毕后,调用save
函数,获取表单行对应信息。
所以总结一下编辑的流程:
- 点击编辑按钮:
- 将正在编辑的
key
值设为当前行,该行由只展示信息改为展示可编辑表单 - 调用
setFieldsValue
,将table
中该行原有信息预填入form
- 将正在编辑的
- 编辑信息
- 点击保存按钮:
- 验证表单数据,并获取到该行已修改的
form
数据 - 找到
table
组件修改行,根据表单新数据更新table
组件数据
- 验证表单数据,并获取到该行已修改的
即:根据table
中数据预填form
,又根据form
的修改更新table
//开始编辑(预填表单数据)
const edit = (record: Partial<Item> & { key: React.Key }) => {
form.setFieldsValue({ ...record });
setEditingKey(record.key);
};
//保存数据
const save = async (key: React.Key) => {
try {
//验证表单,获取表单行数据
const row = (await form.validateFields()) as Item;
//拷贝更改前data数组
const newData = [...data];
//根据传入的key值找到要编辑的数据
const index = newData.findIndex(item => key === item.key);
//如果找到对应行数据(index > -1)
if (index > -1) {
const item = newData[index];
//修改对应位置数据 (item为原始数据,row为更新后数据,使用...语法合并)
newData.splice(index, 1, {
...item,
...row,
});
//更新data数据
setData(newData);
//清空editingKey
setEditingKey('');
messageApi.success('修改成功!');
}
}catch(){}
}
小结
到此已大体实现了编辑节点port
信息的table
组件,关于修改的数据同步图渲染或后端请求的代码这里没有展示,旨在关注antd
如何实现可以edit
的table
组件以及table
和form
两个组件混合使用时,数据是如何流通,如何更新的
0 条评论