Warning: [antdv: Each record in table should have a unique key prop,or set rowKey to an unique primary key.]错误处理
一 项目架构说明
新手直接上手项目,前端采用Vue框架+后端springboot架构。硬着头皮边学边用,天天都是瓶颈。
二 错误现象
在前端页面上打开开发者模式上,看到有报错:
Warning: [antdv: Each record in table should have a unique `key` prop,or set `rowKey` to an unique primary key.] warning @ warning.js?2149:7 warning.js?2149:7 Warning: [antdv: Table] Each record in dataSource of table should have a unique `key` prop, or set `rowKey` of Table to an unique primary key, warning @ warning.js?2149:7

三 错误分析和处理
请教前端老鸟同事,说是前端页面上,组件上少了key和rowKey的属性设置。
1出错的前端页面代码:
<a-table
:columns="columns"
:dataSource="data"
:loading="loading"
:pagination="pagination"
bordered
size="small"
@change="handleTableChange"
>
<template slot="action" slot-scope="text, record">
<a-button
icon="edit"
type="primary"
class="btn_margin"
@click="showModal(record, 'edit')"
>编辑</a-button>
<a-button
icon="copy"
type="primary"
class="btn_margin"
@click="showModal(record, 'copy')"
>复制</a-button>
<a-button
icon="delete"
type="danger"
class="btn_margin"
@click="deleteConfirm(record)"
>删除</a-button>
</template>
</a-table>
...
其它代码
...
let columnsCN = [
{
title: "配置类型编码",
dataIndex: "typeCode",
align: "center"
},
{
title: "配置类型名称",
dataIndex: "typeName",
align: "center"
},
{
title: "配置名称",
dataIndex: "name",
align: "center"
},
{
title: "配置值",
dataIndex: "value",
align: "center",
ellipsis: true,
},
{
title: "备注",
dataIndex: "remark",
align: "center"
},
{
title: "操作",
key: "action",
scopedSlots: { customRender: "action" },
align: "center",
width: 300
}
];2 修改后的代码:
<a-table
:columns="columns"
:dataSource="data"
:loading="loading"
:pagination="pagination"
rowKey="id"
bordered
size="small"
@change="handleTableChange"
>
<template slot="action" slot-scope="text, record">
<a-button
icon="edit"
type="primary"
class="btn_margin"
@click="showModal(record, 'edit')"
>编辑</a-button>
<a-button
icon="copy"
type="primary"
class="btn_margin"
@click="showModal(record, 'copy')"
>复制</a-button>
<a-button
icon="delete"
type="danger"
class="btn_margin"
@click="deleteConfirm(record)"
>删除</a-button>
</template>
</a-table>
...
其它代码
...
let columnsCN = [
{
title: "配置类型编码",
dataIndex: "typeCode",
align: "center",
key: "typeCode"
},
{
title: "配置类型名称",
dataIndex: "typeName",
align: "center",
key: "typeName"
},
{
title: "配置名称",
dataIndex: "name",
align: "center",
key: "name"
},
{
title: "配置值",
dataIndex: "value",
align: "center",
ellipsis: true,
key: "value"
},
{
title: "备注",
dataIndex: "remark",
align: "center",
key:"remark"
},
{
title: "操作",
dataIndex: "action",
key: "action",
scopedSlots: { customRender: "action" },
align: "center",
width: 300,
}
];修改保存之后,直接在Chrome上,打开控制台,不再有上述错误出现。
四 为什么要这么修改就不报错呢
指出问题错误的过程中,同事让我打开AntDesignVue的官网,然后搜索table组件,链接到
https://antdv.com/components/table/#Table
尝试找到页面上的一个例子,点开代码,然后,看到官方的规范写法:

然后,在官网的该页面的最下方:
Note #
The values inside dataSource and columns should follow this in Table, and dataSource[i].key would be treated as key value default for dataSource.
If dataSource[i].key is not provided, then you should specify the primary key of dataSource value via rowKey. If not, warnings will show in browser console.
// primary key is uid
return <Table rowKey="uid" />;
// or
return <Table rowKey={record => record.uid} />;在项目的Chrome控制台界面,进入network选项卡,找到访问路径,点击preview,看到后端给的数据是

也就是说,前端页面上的table组件里面的数据来源于 :dataSource=”data”

data又是这个数组:

该数组最终由后端接口提供:

最后,进入后端程序中查看Controller:
@RestController(value = "配置管理")
@RequestMapping(value = "/config")
@Api(tags = "config", value = "配置管理")
public class SamConfigController {
@Autowired
private ISamConfigManageService samConfigManageService;
@RequestMapping(value = "/list", name = "配置列表")
@ApiOperation(value = "配置列表", notes = "")
@VerifyToken
public SamResponseVO list(@RequestBody PageVO<SamConfigVO> condition) {
SamResponseVO samResponseVO = new SamResponseVO();
samResponseVO.setRespSuccess(true);
Page page = samConfigManageService.queryConfigPageList(condition);
samResponseVO.setData(page);
return samResponseVO;
}返回值是一个SamResponseVO类型,再进去看,是把Page对象封装到SamResponseVO的data字段里了。进而查看Page对象的定义:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.onlyou.framework.mybatis.dao.pojo;
import java.io.Serializable;
import java.util.Collections;
import java.util.List;
public class Page implements Serializable {
private static final long serialVersionUID = -4313766497412557907L;
private int pageSize = 10;
private int totalRecord = 0;
private int currentPage = 1;
private int totalPage;
private List records = Collections.emptyList();
public Page() {
}
public Page(int currentPage, int pageSize) {
this.currentPage = currentPage;
this.pageSize = pageSize;
}
public Page(int currentPage, int totalRecord, int pageSize) {
this.currentPage = currentPage;
this.totalRecord = totalRecord;
this.pageSize = pageSize;
}
public int getRecordStart() {
return this.currentPage > 0 ? (this.currentPage - 1) * this.pageSize + 1 : 0;
}
public int getRecordStartPrev() {
return this.currentPage > 0 ? (this.currentPage - 1) * this.pageSize : 0;
}
public int getRecordEnd() {
return this.currentPage > 0 ? this.currentPage * this.pageSize : 0;
}
public int getPageSize() {
return this.pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
public int getTotalRecord() {
return this.totalRecord;
}
public void setTotalRecord(int totalRecord) {
this.totalRecord = totalRecord;
}
public int getCurrentPage() {
return this.currentPage;
}
public void setCurrentPage(int currentPage) {
this.currentPage = currentPage;
}
public int getTotalPage() {
this.totalPage = (int)Math.floor((double)this.totalRecord * 1.0D / (double)this.pageSize);
if (this.totalRecord % this.pageSize != 0) {
++this.totalPage;
}
return this.totalPage == 0 ? 1 : this.totalPage;
}
public <T> List<T> getRecords() {
return this.records;
}
public <T> void setRecords(List<T> records) {
this.records = records;
}
public static class FieldDomain {
public static final String RECORD_START = "recordStart";
public static final String RECORD_START_PREV = "recordStartPrev";
public static final String RECORD_END = "recordEnd";
public static final String PAGE_SIZE = "pageSize";
public static final String TOTAL_RECORD = "totalRecord";
public static final String CURRENT_PAGE = "currentPage";
public static final String TOTAL_PAGE = "totalPage";
public static final String RECORDS = "records";
public FieldDomain() {
}
}
}最终,我们知道前端页面的data的数据来源了。
{respSuccess: true, respMsg: null, respCode: null, rcvDate: null, rcvTime: null,…}
data: {pageSize: 10, totalRecord: 74, currentPage: 1, totalPage: 8,…}
currentPage: 1
pageSize: 10
recordEnd: 10
recordStart: 1
recordStartPrev: 0
records: [{rowState: 0, id: "727C0EZOVYGKSLPJBXFG7AHATMP2N3IE", typeCode: "VERIFY_DELIVERY_STATUS",…},…]
0: {rowState: 0, id: "727C0EZOVYGKSLPJBXFG7AHATMP2N3IE", typeCode: "VERIFY_DELIVERY_STATUS",…}
1: {rowState: 0, id: "I97CYG4CLHK55PRY8MQCLK97E430ZYJC", typeCode: "DEVICE_BILL_NUM", typeName: "票箱统计数量",…}
2: {rowState: 0, id: "42QPPVPIR6HRK9HW4UTSLUNSHX5H3U5W", typeCode: "DEVICE_REMIB_NUM",…}
3: {rowState: 0, id: "UXURZ9E4D9IV6BQYTSR940SD53JIZMY4", typeCode: "DEVICE_BILL_NUM", typeName: "票箱累计数量",…}
4: {rowState: 0, id: "51CYIKWS3B7OT0J89PDM7O003HMS3IRS", typeCode: "DEVICE_SEND_EMAIL",…}
5: {rowState: 0, id: "AMMOYZRMA4ZVQPJVAEDKAPBRI5VV33EW", typeCode: "DEVICE_SEND_EMAIL",…}
6: {rowState: 0, id: "3750879f0a4241308818e17eaa921751", typeCode: "MONITOR_ERROR_CODE",…}
7: {rowState: 0, id: "044775c953a6442f8d1c323c3f259435", typeCode: "MONITOR_ERROR_CODE",…}
8: {rowState: 0, id: "b7206ea918d84012a3773abfc8d9ce5d", typeCode: "MONITOR_ERROR_CODE",…}
9: {rowState: 0, id: "b03d734f2e1c4a158664b187e035bcc2", typeCode: "MONITOR_ERROR_CODE",…}
totalPage: 8
totalRecord: 74
rcvDate: null
rcvTime: null
respCode: null
respMsg: null
respSuccess: true所以,我们把前端页面上table组件上加上rowKey=”id才解决了这个问题。
一条评论
Pingback: