MiniUI datagrid 的客戶端分頁解決方案

喜歡就請關注 ↑↑↑  邊城客棧 ↑↑↑

官方的解決方案

官方在“[在線示例][officialdemo]”中給了一個簡單的 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 中
    }
}

設置和解除客戶端分頁

其中 setupdestroy 爲分別爲 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;
    }

來自官方示例中的關鍵代碼

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);
    }

改寫 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();
                }
            });
    }

改寫 setData

setData 也進行了替換,參數是整表的數據,但只能顯示當前頁的數據

    setData(data) {        const rows = Array.isArray(data)
            ? data
            : (data.data || []);        this._data = rows;        this.setPageData(this._datagrid.getPageIndex(), this._datagrid.getPageSize());
    }

應用

爲了方便封裝,再加一個靜態方法

    static wrap(datagrid) {        return new ClientPagination(datagrid);
    }

現在只需要在頁面初始化(或其它合適的初始化位置)加上

ClientPagination.wrap(mini.get("datagridId"));

如果需要 destroy,可以這樣

var cpBlabla = ClientPagination.wrap(mini.get("datagridId"));
....
cpBalbal.destory();

小結

通過 ClientPagination 的封裝,不需要改變原有的業務代碼和設置,就可以實現 miniui datagrid 的客戶端分頁。

但是這個實現只是解決了當前的問題,如果有新的需求:

當頁碼在前三頁的時候用客戶端分頁,以減少數據加載次數,翻到後面的時候需要用服務器端分頁

又該如何?是不是該出個續集?

請通過下面的“原文鏈接”,在原文文末找到續集鏈接

本文分享自微信公衆號 - 邊城客棧(fancyidea-full)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章