miniui datagrid 的客户端分页解决方案
在前一篇,miniui datagrid 的客户端分页解决方案 中留下了一个问题:如果前三页只需要加载一次数据,采用客户端分页,之后的每一页都仍然从服务器获取,应该怎么办?
现在就来改造 ClientPagination
,让它实现这个功能。不过在此之前需要先解决上个版本中存在的一个问题:destroy()
并没有完全恢复原有的 datagrid 对象,因为忘了在 destroy()
中恢复 this._datagrid
的 load
和 setData
函数。
修复一个重要的 BUG
很容易想到下面的代码,不过下面这个代码是个错误的实现
destroy() {
// 错误的实现
METHODS.forEach(name => {
this._datagrid[name] = this._origin[name];
});
// ....
}
别忘了 this._origin
中保存的每个函数都是通过原函数 bind()
而来,所以其实已经不是原函数了。此外,也很容易想到 miniui 的 datagrid 多半是类实现,原来的 load()
和 setData()
应该来源于原型。所以正确的解决办法是
// in destroy's body
METHODS.forEach(name => {
delete this._datagrid[name];
});
试验一下就能证明它的正确性。
改造 ClientPagination
修改 beforeload 事件处理
原来在 beforeload
事件中直接设置了 event.cancel = true
来避免远程加载,但现在情况发生了变化,只需要在页码小于3(miniui 的 pageIndex
是从 0 开始)避免远程加载,大于等于 3 的时候仍然需要从服务器获取页面数据,所以
const pageIndex = e.data.pageIndex;
if (pageIndex < 3) {
e.cancel = true;
} else {
// 直接返回,不需要通过 setPageData() 来设置页面数据
return;
}
接下来该干啥
接下来还有一些细节需要处理。
首先就是 setData
中没有使用数据的 total
值,因为原来纯客户端分页的时候,数据总行数就是 total
值,但现在不一样了,设置的数据不一定是所有数据,所以需要先检查 total
,如果没有 total
再使用 rows.length
代替。
setData(data) {
const rows = Array.isArray(data)
? data
: (data.data || []);
this._data = rows;
this._total = data.total || rows.length;
this.setPageData(this._datagrid.getPageIndex(), this._datagrid.getPageSize());
}
相应的,还需要在 setPageData()
里修改一个 setTotalCount()
的实参
grid.setTotalCount(this._total);
搞定!
源代码
ClientPagination mini_clientpagination.js
const METHODS = ["setData", "load"];
class ClientPagination {
static wrap(datagrid) {
return new ClientPagination(datagrid);
}
constructor(datagrid) {
this._datagrid = datagrid;
this._origin = {};
this.setup();
}
setup() {
const grid = this._datagrid;
const origin = this._origin = {};
METHODS.forEach(name => {
// 绑定到原对象的原方法
origin[name] = grid[name].bind(grid);
// 替换为本类中定义的新方法
grid[name] = this[name].bind(this);
});
// 暂存事件处理函数,以便后面注销
this._onBeforeLoad = this.onBeforeLoad.bind(this);
grid.on("beforeload", this._onBeforeLoad);
}
destroy() {
// 恢复原方法(来自 datagrid 类的 prototype)
// 只需要删除附加到对象上的同名方法即可
METHODS.forEach(name => {
delete this._datagrid[name];
});
this._origin = {};
this._datagrid.un("beforeload", this._onBeforeLoad);
this._datagrid = null;
}
onBeforeLoad(e) {
// 根据官方的解决方案而来
const pageIndex = e.data.pageIndex;
if (pageIndex < 3) {
e.cancel = true;
} else {
// NOTE 一般来说不需要修改 url,直接通过 pageIndex 参数即可从后台获取不同页的数据
// 这里因为是用的静态 JSON 数据,所以需要修改 url
e.url = `data/page${pageIndex}.json`;
return;
}
let pageSize = e.data.pageSize;
this.setPageData(pageIndex, pageSize);
}
load(params, success, fail) {
const grid = this._datagrid;
const pageIndex = grid.getPageIndex();
const url = grid.getUrl();
params = $.extend(pageIndex < 3
? {}
: { pageIndex: pageIndex, pageSize: grid.getPageSize() },
params);
const settings = {
type: "get",
dataType: "json",
data: params
};
$.ajax(url, settings)
.then(data => {
this.setData(data);
if (typeof success === "function") {
success(data);
}
}, () => {
if (typeof fail === "function") {
fail();
}
});
}
setData(data) {
const rows = Array.isArray(data)
? data
: (data.data || []);
this._data = rows;
this._total = data.total || rows.length;
this.setPageData(this._datagrid.getPageIndex(), this._datagrid.getPageSize());
}
setPageData(pageIndex, pageSize) {
const allData = this._data;
let start = pageIndex * pageSize;
if (start >= allData.length) {
start = 0;
pageIndex = 0;
}
const end = Math.min(start + pageSize, allData.length);
const pageData = [];
for (let i = start; i < end; i++) {
pageData.push(allData[i]);
}
const grid = this._datagrid;
grid.setTotalCount(this._total);
grid.setPageIndex(pageIndex);
grid.setPageSize(pageSize);
this._origin.setData(pageData);
}
}
测试页面 index.html
<!DOCTYPE html />
<html>
<head>
<title>Client Pagination</title>
<link href="miniui/themes/default/miniui.css" rel="stylesheet" type="text/css" />
<style type="text/css">
body {
margin: 0;
padding: 5px;
}
</style>
</head>
<body>
<div class="mini-fit">
<div class="mini-datagrid" id="grid" pageSize="6" url="data/all.json" style="height: 100%">
<div property="columns">
<div type="indexcolumn"></div>
<div type="checkboxcolumn" width="32"></div>
<div field="order" width="60">序号</div>
<div field="name" width="80">名称</div>
<div field="autofit" width="auto"></div>
</div>
</div>
</div>
<script src="jquery-1.6.2.min.js"></script>
<script src="miniui/miniui.js"></script>
<script src="mini_clientpagination.js"></script>
<script>
mini.parse();
const grid = mini.get("grid");
ClientPagination.wrap(grid);
grid.load();
</script>
</body>
</html>
当然还有 data 目录下的数据源 all.json
和 page3.json
all.json 中是前3页的数据
page3.json 中是第4页的数据
数据结构(仅结构
{
"data": [],
"total": 50
}
其中 data
的每一项结构
{
"order": 1108,
"name": "bridge"
}
完整的测试数据就自己拼吧,当然最好是还是用服务端数据,通过参数来返回不同的数据行。
我懒,懒得为了个实验还去写服务端
更多建议: