官方的解决方案
官方在“在线示例”中给了一个简单的 client pagination 解决方案,代码就不贴了,这里说说它的基本思想和处理过程。
首先,是绑定一个 preload 事件,在这个事情中设置 event.cancel = true,阻止 datagrid 在翻页的时候向服务器请求加载数据。
那么数据从哪来呢?当然只有在外部写一个 ajax 过程获取了。不过取得的数据并不直接交给 datagrid,而是缓存起来,放在dataResult 中。
现在继续说 preload,除了阻止 datagrid 向服务器请求数据之外,preload 还需要从缓存中找到页码对应的数据行,通过 setData设置给 datagrid 渲染出来。OK,这个事情是交给自定义函数 fillData 来实现的。当然,这里还要干点与页面相关的事情,就是setTotalCount()、setPageIndex() 和 setPageCount()。
官方的解决方案展示了 miniui datagrid 客户端分页解决方案的基本思想,但是这个示例太过简单。如果,想把之前的服务端分页改成客户端分页该怎么办?原来已经存在对 load()、setData() 等的调用,现在怎样以最小的代码改动来实现客户端分页?
class ClientPagination
就上面的问题,首先能想到的,就是保留原有 load() 和 setData() 的接口,但是要改变它们的行为。
- load() 原有行为是提交了〔XXX参数〕,从服务器加载指定页的数据;而现在需要改为加载所有数据。
- setData() 原来是向 datagrid 设置了需要显示的所有数据行,不管分页(比如可以一次显示出来200条数据);而现在,如果设置的数据量过大,则需要通过客户端分页来逐页显示。
JavaScript 语言是动态有,这使得替换方法成为可能,这是很多静态语言做不到的事情。而替换方法也是解决这个问题时最容易想到的办法。当然除此之外,还得庆幸 miniui 没有采用 jQuery 扩展的方式(如 $grid.datagrid("setData", ...))来实现组件。
替换方法成为可能,但是原有方法还是得保留,因为我们需要通过原有方法来操作 datagrid。所以 ClientPagination 类应该是这个样子:
ClientPagination 的基本结构
注:本文中所有代码都是 ES6 语法
const METHODS = ["setData", "load"];
class ClientPagination {
constructor(datagrid) {
this._datagrid = datagrid;
this._origin = {};
this.setup();
}
setup() {
// TODO 暂存 this._datagrid 的 load、setData 等方法
// 并为 this._datagrid 设置新方法和注册事件
}
destroy() {
// TODO 恢复 this._datagrid 的方法,注销事件
}
onBeforeLoad() {
// 根据官方的解决方案而来
e.cancel = true;
let pageIndex = e.data.pageIndex;
let pageSize = e.data.pageSize;
this.setPageData(pageIndex, pageSize);
}
// 参照 datagrid.load 的函数签名
load(params, success, fail) {
// TODO 实现加载数据,并保存到 this._data 中
// 然后调用 this.setData() 保存和显示数据
}
setData(data) {
// TODO 保存 data 到 this._data 中,
// 然后调用 this.setPageData() 显示当前页的数据
}
setPageData(pageIndex, pageSize) {
// TODO 从缓存的 this._data 中按 pageIndex 和 pageSize 取出要显示的数据行
// 然后通过 this._origin.setData() 设置在 datagrid 中
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
设置和解除客户端分页
其中 setup 和 destroy 为分别为 datagrid 绑定和解绑客户端分页处理
setup() {
const grid = this._datagrid;
const original = 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() {
this._origin = {};
this._datagrid.un("beforeload", this._onBeforeLoad);
this._datagrid = null;
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
来自官方示例中的关键代码
onBeforeLoad 以及 setPageData 是参照官方解决方案中的 beforeload 事件和 fillData 方法写的。onBeforeLoad 的代码在上面已经有了,现在是 setPageData 的代码
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(allData.length);
grid.setPageIndex(pageIndex);
grid.setPageSize(pageSize);
this._origin.setData(pageData);
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
改写 load
load 方法需要用 ajax 调用来替换原来的 load 方法
load(params, success, fail) {
const grid = this._datagrid;
const url = grid.getUrl();
const settings = {
type: "post",
dataType: "json",
data: params || {}
};
$.ajax(url, settings)
.then(data => {
this.setData(data);
if (typeof success === "function") {
success(data);
}
}, () => {
if (typeof fail === "function") {
fail();
}
});
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
改写 setData
而 setData 也进行了替换,参数是整表的数据,但只能显示当前页的数据
setData(data) {
const rows = Array.isArray(data)
? data
: (data.data || []);
this._data = rows;
this.setPageData(this._datagrid.getPageIndex(), this._datagrid.getPageSize());
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
应用
为了方便封装,再加一个静态方法
static wrap(datagrid) {
return new ClientPagination(datagrid);
}
- 1.
- 2.
- 3.
现在只需要在页面初始化(或其它合适的初始化位置)加上
ClientPagination.wrap(mini.get("datagridId"));
- 1.
如果需要 destroy,可以这样
var cpBlabla = ClientPagination.wrap(mini.get("datagridId"));
....
cpBalbal.destory();
- 1.
- 2.
- 3.
小结
通过 ClientPagination 的封装,不需要改变原有的业务代码和设置,就可以实现 miniui datagrid 的客户端分页。
但是这个实现只是解决了当前的问题,如果有新的需求:
当页码在前三页的时候用客户端分页,以减少数据加载次数,翻到后面的时候需要用服务器端分页