表格嵌套和子表展开的处理
在antd官网中有表格嵌套的案例:https://ant.design/components/table-cn/#components-table-demo-nested-table,而实际中子表的展开往往是通过异步请求来进行查看。
首先子表的渲染是通过expandedRowRender处理,父表如下方所示。
<Table
columns={columns} size="small"
className="components-table-demo-nested"
expandedRowRender={expandedRowRender}
dataSource={dataSource}
bordered
/>
刚开始我是根据官方示例所写,后来为了异步请求,我就把expandedRowRender函数表达式写成了声明函数,即expandedRowRender={this.expandedRowRender.bind(this}
,把异步请求写在里面。然后就发现一个问题,这样一写,expandedRowRender就像变成了一个定时器一样,一直在往后台发送请求,就没停下来过。不一会儿,你就能收到后端大哥的黑人问号脸。很明显,子表的渲染进入了一个死循环。
但是表格的嵌套又必须完成,终于,我发现了一个可以控制的方法:onExpand。于是expandedRowRender还是和官方那样写成函数表达式,父表进一步改成下方所示:
<Table
columns={columns} size="small"
className="components-table-demo-nested"
expandedRowRender={expandedRowRender}
onExpand={this.onExpand.bind(this)}
dataSource={dataSource}
bordered
/>
然后我们就可以在onExpand里面写异步请求了。示例如下:
onExpand(expanded, record) {
if (!expanded) {
return;
}
axios.post(url).then((res) => {
let childData = res.data.data;
if (childData !== null) {
this.setState({ childData })
}
}).catch((e) => { });
}
展开子表,触发onExpand,请求到数据childData后,子表渲染expandedRowRender示例如下:
const expandedRowRender = () => {
return <Table columns={childColums}
size="small"
dataSource={childData}
pagination={false} />;
};
这个时候,表格嵌套就已经完成了。可是还是没那么简单,你会发现,打开第一行后,数据还是正常的,但是打开第二行后,第一行子表的数据居然也变成了第二行子表的数据了。很明显第一行子表的数据又被重新渲染了。如下图描述:

可是实际中两个子表的数据是不一样的,不能让这种Bug发生,然后我就想了个折中的办法:每次打开只能展开一个子表,如果展开另一个子表,那么其他子表都要关闭。这样,页面上只渲染了一个子表的数据,也避免子表展开过多影响查看。
查看了官网文档后,我发现了一个属性expandedRowKeys,官方描述这个是可以控制展开和收起的。然后我把父表进一步改成了下方所示:
<Table
columns={columns} size="small"
className="components-table-demo-nested"
expandedRowRender={expandedRowRender}
onExpand={this.onExpand.bind(this)}
dataSource={dataSource}
expandedRowKeys={this.state.expandedRowKeys}
bordered
/>
注意它是通过一个数组来控制的,而且下标是从1开始。那么方法onExpand也要经过一下改造,对变量expandedRowKeys来进行控制:
onExpand(expanded, record) {
if (!expanded) {
// 关闭时把所有子表关上
this.setState({
expandedRowKeys: []
});
return;
}
// 打开时只打开当前数据,这里index是我手动往数据里面加的。就是父表数据请求过来的时候,循环一下加入一个index,下标从1开始。
this.setState({
expandedRowKeys: [record.index]
})
axios.post(url).then((res) => {
let childData = res.data.data;
if (childData !== null) {
this.setState({ childData })
}
}).catch((e) => { });
}
好了,到了这一步,我天真的以为问题已经解决了,然而只是表面。当父表数据过多并加入翻页后,我又发现一个bug,那就是翻页后子表打不开!!!没关系,继续来解决。首先加入了翻页的父表如下所示:
<Table
columns={columns} size="small"
className="components-table-demo-nested"
expandedRowRender={expandedRowRender}
onExpand={this.onExpand.bind(this)}
dataSource={dataSource}
expandedRowKeys={this.state.expandedRowKeys}
bordered
onChange={this.pageChange.bind(this)}
/>
所以每次翻页都要把expandedRowKeys清空一下:
// 更改页数
pageChange(e) {
this.setState({
current: e.current,
pageSize: e.pageSize,
expandedRowKeys: [], // 每次翻页把所有子表关闭
}, () => {
this.updatePage(); // 请求数据请求更新页面
})
}
在updatePage函数里,请求的是父表的数据,我们需要对下标index(关系子表展开的那个值)进行处理,而不是之前简单循环之后data.index = index + 1。利用页码current和每页数量pageSize,计算如下:
res.data.map((val,ind)=>{
val.index = this.state.pageSize * (this.state.current - 1) + (ind + 1);
})
到此,真的是全部解决!!!