DataTables API及服务端处理模式介绍和后端分页案例
一.DataTables概述
Datatables是一款jquery表格插件.是一个高度灵活的工具,可以将任何HTML表格添加高级的交互功能.支持
- 分页,即时搜索和排序
- 支持数据源: DOM, JavaScript, Ajax 和服务器处理
- 支持不同的主题: DataTables, jqueryUI, Bootstrap, Foundation
- 支持国际化和多样的option选项
官方网址:
- http://datatables.club/ : 中文官网
- http://datatables.club/manual/ : datatables基本手册
- http://datatables.club/reference/ : 在线API
1.处理数据的三个核心概念
1.1 处理模式(processing modes)
DataTables 中有两种不同的方式处理数据(排序、搜索、分页等):
- 客户端处理(Client)—— 所有的数据集预先加载(一次获取所有数据),数据处理都是在浏览器中完成的【逻辑分页】, 适用于数据量小。
- 服务器端处理(ServerSide)—— 数据处理是在服务器上执行(页面只处理当前页的数据)【物理分页】,适用于数据量大。
注意:两种处理模式不能同时使用,但是可以动态更改从一个模式到另一个。
1.2 数据源类型(data source types)
DataTables 使用的数据源必须是一个数组,数组里的每一项将显示在你定义的行上面,DataTables 可以使用三种基本的 JavaScript 数据类型来作为数据源:
- 数组(Array [])
- 对象(object {}) -- 常使用
- 实例(new myclass())
1.3 数据源(data sources)
DataTables 支持三种数据源显示:
- DOM
- Javascript
- Ajax -- 常使用
2.安装datatables插件
- 安装页: http://datatables.club/manual/install.html
- 下载地址: http://datatables.net/releases/DataTables-1.10.13.zip
将media文件下的内容全部放到工程下,html页面引入即可使用:
<!-- jQuery -->
<script type="text/javascript" src="media/js/jquery.js"></script>
<!-- DataTables -->
<link rel="stylesheet" type="text/css" href="media/css/jquery.dataTables.css">
<script type="text/javascript" src="media/js/jquery.dataTables.js"></script>
- 下载bootstrap样式: https://v3.bootcss.com/getting-started/
- 下载datatables的bootstrap的样式: http://datatables.club/manual/styling/bootstrap.html
继续在html页面引入datatables的bootstrap样式:
<!-- CSS -->
<link rel="stylesheet" type="text/css" href="http://netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="media/css/dataTables.bootstrap4.css">
<!-- jQuery -->
<script type="text/javascript" src="media/js/jquery.js"></script>
<!-- dataTables.js -->
<script type="text/javascript" src="media/js/jquery.dataTables.js"></script>
<script type="text/javascript" src="media/js/dataTables.bootstrap4.js"></script>
3.Option API
文档: http://datatables.club/reference/option/
国际化zh_CN.json文件:
{
"sProcessing": "处理中...",
"sLengthMenu": "显示 _MENU_ 项结果",
"sZeroRecords": "没有匹配结果",
"sInfo": "显示第 _START_ 至 _END_ 项结果,共 _TOTAL_ 项",
"sInfoEmpty": "显示第 0 至 0 项结果,共 0 项",
"sInfoFiltered": "(由 _MAX_ 项结果过滤)",
"sInfoPostFix": "",
"sSearch": "搜索:",
"sUrl": "",
"sEmptyTable": "表中数据为空",
"sLoadingRecords": "载入中...",
"sInfoThousands": ",",
"oPaginate": {
"sFirst": "首页",
"sPrevious": "上页",
"sNext": "下页",
"sLast": "末页",
"sJump": "跳转"
},
"oAria": {
"sSortAscending": ": 以升序排列此列",
"sSortDescending": ": 以降序排列此列"
}
}
常用Option API介绍:
$(function() {
$("#table_id").DataTable({
language: { //异步加载翻译,支持国际化
url:"dist/zh_CN.json" //在static资源文件夹下
},
info: true, //显示页脚信息
lengthChange: true, //显示更换记录数下拉选
aLengthMenu : [6, 16, 32, 64], //更改显示记录数选项
iDisplayLength : 6, //默认显示的记录数
searching: false, //禁用原生搜索
filter: false, //去掉搜索框方法,过滤功能
ordering: true, //开启排序
orderMulti: false, //禁用多列排序
order: [[ 3, "desc" ]], //取消默认排序查询,否则复选框一列会出现小箭头
buttons: ['copy', 'excel', 'pdf'] //开启下载数据为各种格式按钮
renderer: "bootstrap", //渲染样式:Bootstrap和jquery-ui
stripeClasses: ["odd", "even"], //为奇偶行加上样式,兼容不支持CSS伪类的场合
paging: true, //开启分页
pageLength: 3, //每页显示3条数据,pagesize
pagingType: "simple_numbers", //分页样式:simple,simple_numbers,full,full_numbers
scrollX: true, //启动水平滚动
sScrollX : 820, //指定DataTables的宽
scrollY: true, //启动垂直滚动
sScrollY : 560, //指定DataTables的高
autoWidth: false, //禁用自动调整列宽
processing: true, //显示加载提示,自行处理
serverSide: true, //开启服务器端分页
ajax: { //使用ajax异步请求的方式加载数据(包括处理分页,排序,搜索)
url: '/data-source', //请求的URL地址
type: 'GET' //使用GET请求的方式
},
columns: [ // 指定数据源,与服务器端响应的数据一一对应,使用对象数组
{ data: 'buildName', width: "60px", defaultContent: "<i></i>"},
{ data: 'totalCases', width: "120px", defaultContent: "<i></i>"},
{ data: 'failCases', width: "120px", defaultContent: "<i></i>"},
{ data: 'passRate', width: "120px", defaultContent: "<i></i>"},
{ data: 'detail', width: "120px", defaultContent: "<i></i>"},
{ data: 'loginfo', width: "120px", defaultContent: "<i></i>"}
],
columnDefs: [{
"targets": 'nosort', //指定class为nosort的列
"orderable": false, //包含上样式名‘nosort'的禁止排序
"data": 'loginfo', //指定引用loginfo的数据源
render: function (data, type, row) {
//可对数据进行处理,再显示
var html = '<a href="'+data+'" target="_blank" class="btn btn-success btn-sm">logInfo</a>';
return html;
}
}],
});
});
二.服务器处理模式
表格数据量如果很大,超过5000行的话,建议使用后台分页功能。关于这个功能的一些参数:
-
开关:表格初始化时的选项,字段为serverSide,true表示使用后台分页功能
-
datatables发送的数据:使用ajax自定义的方法时, data字段增加了下面三个属性, start和length其实对应的是sql语句limit之后的两个参数
描述 字段 draw 绘制计数器。这个是用来确保Ajax从服务器返回的是对应的(Ajax是异步的,因此返回的顺序是不确定的)。 要求在服务器接收到此参数后再返回 start 第一条数据的起始位置,比如0代表第一条数据 length 告诉服务器每页显示的条数,这个数字会等于返回的 data集合的记录数,可能会大于因为服务器可能没有那么多数据。这个也可能是-1,代表需要返回全部数据(尽管这个和服务器处理的理念有点违背) 其它 还有一些字段指定了哪些列需要排序 和 搜索条件等 -
datatables接受的数据:要求4个参数,分别是:
- draw,请求时的标记字段 ,直接返回
- recordsTotal,表格总长度,总记录数
- recordsFiltered,根据返送的过滤条件 过滤后的数据数,通常可以直接和recordsFiltered相同
- data,表中中需要显示的数据。这是一个对象数组,也可以只是数组,区别在于 纯数组前台就不需要用 columns绑定数据,会自动按照顺序去显示 ,而对象数组则需要使用 columns绑定数据才能正常显示。
需要注意的是,对于后台返回空时,需要将表格长度置为0,否则表格下面的分页按钮不会变化,延续上次结果。
1.html页面中table
<!-- 冒烟 -->
<div class="row" id="pre_alpha_smoke">
<div class="col-md-12">
<h4 class="text-center bg-secondary" style="margin: 24px 0px;">冒烟</h4>
<table id="pre_alpha_smoke_table" class="table table-bordered table-striped" style="width: 100%;">
<thead align='center' valign="middle">
<tr>
<th>版本名称</th>
<th>总用例数</th>
<th>失败用例数</th>
<th>脚本通过率</th>
<th>测试结果</th>
<th>日志信息</th>
</tr>
</thead>
<tbody align='center' valign="middle">
<tr>
<td>1902v52</td>
<td>188</td>
<td>16</td>
<td><span class="badge bg-success">100%</span></td>
<td>ok</td>
<td><a href="http://10.150.99.10:8080/jenkins/job/SMOKE_TEST_5/926/consoleText" target="_blank">logLink</a></td>
</tr>
</tbody>
</table>
</div>
<!-- /.col -->
</div>
<!-- /.row -->
2.table的js文件
$(function () {
var catalog = 'smoke';
var project = 'X653C-H6114GH-PGo';
var starttime = '2019-08-30 00:00:01';
var endtime = '2020-01-16 23:59:59';
//封装相应的请求参数,这里获取页大小和当前页码
// var param = {};
// param.pagesize = data.length;//页面显示记录条数,在页面显示每页显示多少项的时候,页大小
// param.start = data.start;//开始的记录序号
// param.draw = data.draw;//标记字段
// param.currentPage = (data.start) / data.length + 1;//当前页码
$("#pre_alpha_smoke_table").DataTable({
// 语言国际化
language: {
url:"dist/zh_CN.json"
},
info : true,
paging: true,
aLengthMenu : [6, 16, 32, 64], //更改显示记录数选项
iDisplayLength : 6, //默认显示的记录数
// pageLength: 3, //每页显示3条数据
processing: false, //显示加载提示,自行处理
searching: true, //显示原生搜索
stripeClasses: ["odd", "even"], //为奇偶行加上样式,兼容不支持CSS伪类的场合
pagingType: "full_numbers", //分页样式:simple,simple_numbers,full,full_numbers
serverSide: true, //开启服务器模式
//数据来源(包括处理分页,排序,过滤) ,即url,action,接口,等等
ajax: function (data, callback, setting) {
var flag = data.draw;//标记字段
var pagesize = data.length;//当前页大小
var currentPage = (data.start) / data.length + 1;//当前页码
$.ajax({
type: "GET",
url: "http://localhost:8081/api/testresult/"+catalog+"/"+project+"/"+starttime+"/"+endtime+"/"+currentPage+"/"+pagesize,
cache: false, //禁用缓存
data: {}, //传入组装的参数
dataType: "json",
success: function (result) {
console.log(result);
var returnData = {};
// returnData.draw = result.pageInfo.draw;//这里直接自行返回了draw计数器,应该由后台返回,没什么卵用!
returnData.recordsTotal = result.pageInfo.recordsTotal;//返回数据全部记录
returnData.recordsFiltered = result.pageInfo.recordsFiltered;//后台不实现过滤功能,每次查询均视作全部结果
returnData.data = result.pageInfo.data;//返回的数据列表
//此时的数据需确保正确无误,异常判断应在执行此回调前自行处理完毕
callback(returnData);
}
});
},
//使用对象数组,一定要配置columns,告诉 DataTables 每列对应的属性
//data 这里是固定不变的,name,position,salary,office 为你数据里对应的属性
columns: [
{ data: 'buildName' },
{ data: 'totalCases' },
{ data: 'failCases' },
{ data: 'passRate' },
{ data: 'detail' },
{
data: 'loginfo'
/*render: function (data, type, full) {
//可对数据进行处理,再显示
var html = '<a href="'+data+'" target="_blank" class="btn btn-success btn-sm">logInfo</a>';
return html;
}*/
}
],
"columnDefs" : [{
"targets" : 5, //索引从0开始,将索引值所在的列重新定义
"data" : 'loginfo',
render: function (data, type, full) {
//可对数据进行处理,再显示
var html = '<a href="'+data+'" target="_blank" class="btn btn-success btn-sm">logInfo</a>';
return html;
}
}],
//列样式处理
"fnRowCallback": function (nRow, aData, iDisplayIndex, iDisplayIndexFull) {
},
// 切换下一页更新复选框的状态为不选中:重绘的回调函数,更新状态
"fnDrawCallback" : function() {
},
});
})
3.后端Controller类
@RestController
@RequestMapping("/api")
public class SmokeResultController {
private final Logger log = LoggerFactory.getLogger(SmokeResultController.class);
@Autowired
private SmokeResultService smokeResultService;
// 获采用datatables前端插件的服务器端处理模式 -- 后端分页(需要传入currentPage和pageSize)
@GetMapping("/testresult/{catalog}/{project}/{starttime}/{endtime}/{currentPage}/{pagesize}")
public ResponseEntity<TestResultResponse> getPhaseSmokeResult(@PathVariable("catalog")String catalog,@PathVariable("project")String project,@PathVariable("starttime")String starttime,@PathVariable("endtime")String endtime,
@PathVariable("currentPage")Integer currentPage,@PathVariable("pagesize")Integer pagesize){
log.debug("REST request to get {} phase smoke result from {} to {} ", project, starttime, endtime);
TestResultResponse response = smokeResultService.getPhaseSmokeResult(catalog,project,starttime,endtime,currentPage,pagesize);
return new ResponseEntity<>(response, HttpStatus.OK);
}
}
serviceImpl类:
@Service
public class SmokeResultServiceImpl implements SmokeResultService{
@Autowired
private SmokeResultMapper smokeResultMapper;
@Override
public TestResultResponse getPhaseSmokeResult(String catalog, String project, String starttime, String endtime, Integer currentPage, Integer pagesize) {
PageHelper.startPage(currentPage, pagesize);//pageHelper分页插件,只对后续一条语句起作用
List<SmokeResult> smokeResultList = smokeResultMapper.getPhaseSmokeResult(project,starttime,endtime);
PageInfo<SmokeResult> pageInfo = new PageInfo<>(smokeResultList);
TestResultResponse response = new TestResultResponse();
response.setCatalog(catalog).setProject(project).setStarttime(starttime).setEndtime(endtime).setPageInfo(pageInfo);
return response;
}
}
entity类:
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class TestResultResponse {
private String project;
private String version;
private String starttime;
private String endtime;
private String pass;
private String fail;
private String total;
private String catalog;
private PageInfo pageInfo;
}
4.PageInfo类
public class PageInfo<T> {
private Integer draw;//绘制计数器
private Integer start;//每页第一条数据的起始位置
private Integer length;//每页显示的条数
private Long recordsTotal;//总记录数
private Long recordsFiltered;//过滤后的数据数
private List<T> data;//表中需要显示的数据
}
5.效果图
三.客服端处理模式
当数据量比较小时,可以采用datatables自动分页,一次将数据全部获取,分页交给浏览器处理,逻辑分页.这里就不需要开启serverSide和使用自带的ajax请求了.
$(function() {
$("#pre_alpha_smoke_table").dataTable({ // 客户端模式这样初始化配置就可以
language: {
url:"dist/zh_CN.json"
},
"aLengthMenu" : [3, 16, 32, 64], //更改显示记录数选项
"iDisplayLength" : 3, //默认显示的记录数
"lengthChange": false, //禁用更换记录数的下拉选
"searching": false, //禁用搜搜框
});
var starttime = '2020-03-02 01:51:20';
var endtime = '2020-03-02 12:51:20';
ajax({
type: 'get',
dataType: 'json',
url: 'http://localhost:8081/api/testresult/smoke/HIOS6.0-Q-MP-KB8-P70/all',
data: {
'starttime': starttime,
'endtime': endtime
},
cache: false,
async: true,
success: function (data) {
console.log(data);
// 获取项目名称
$("#projectname").html(data.project);
// 获取pre-alpha的monkey上海数据
var monkey_sh = data.detail.results[0];
console.log(monkey_sh);
// 动态生成monkey_sh table中的数据
$("#pre_alpha_monkey_sh_table").dataTable().fnClearTable(); //清空一下table
$("#pre_alpha_monkey_sh_table").dataTable().fnDestroy(); //还原初始化了的datatable
$("#pre_alpha_monkeysh_tbody").empty();
//for(var i=0;i<monkey_sh.length;i++){
var $tr = $("<tr></tr>");
$tr.append("<td>"+monkey_sh.round+"</td>");
$tr.append("<td>"+monkey_sh.version+"</td>");
$tr.append("<td>"+monkey_sh.dppm+"</td>");
$tr.append("<td>"+monkey_sh.samples+"</td>");
$tr.append("<td>"+monkey_sh.HWT+"</td>");
$tr.append("<td>"+monkey_sh.SWT+"</td>");
$tr.append("<td>"+monkey_sh.FatalJE+"</td>");
$tr.append("<td>"+monkey_sh.FatalNE+"</td>");
$tr.append("<td>"+monkey_sh.KE+"</td>");
$tr.append("<td>"+monkey_sh.JE+"</td>");
$tr.append("<td>"+monkey_sh.NE+"</td>");
$tr.append("<td>"+monkey_sh.ANR+"</td>");
$("#pre_alpha_monkeysh_tbody").append($tr);
//}
//dataTable重新渲染
$("#pre_alpha_monkey_sh_table").dataTable({
language: {
url:"dist/zh_CN.json"
},
"aLengthMenu" : [3, 16, 32, 64], //更改显示记录数选项
"iDisplayLength" : 3, //默认显示的记录数
"lengthChange": false,
"searching": false,
});
},
error: function (msg) {
alert("网络延迟,请待会加载......");
}
});
})