[Kendo] KendoGrid 使用「KendoGridRequest」來達成Pagger, Sort 的功能!

公司產品使用Kendo 來製作前端畫面已經一段時間,其中Kendo Grid 更是大量使用,

基於效能與使用性的考量,Grid 的部分還自行客製了「分頁」與「排序」的功能,

因此就自行寫了許多Code 來達成這些目的,當然在一次次的維護與除錯後,

這些Code 變得又臭又長且非常難以維護,每次碰到問題時接手的人都要花很多時間來看這些Code。

最近同事有提出Kendo 本身就有提供「KendoGridRequest」來協助我們完成「分頁」與「排序」的功能。

參考完黑暗執行緒的「在ASP.NET 4中使用Kendo UI Grid」,發現他在參數的部分是直接使用KendoGridRequest 來接,額外的參數就接在後面繼續傳。

//這段Code是從「黑暗執行緒」-在ASP.NET MVC 4中使用Kendo UI Grid 
//擷取下來的。       
public JsonResult Grid(KendoGridRequest request, string keywd)
{
  var result = SimMemberInfo.SimuDataStore.Where(o =>
  string.IsNullOrEmpty(keywd) || o.UserName.Contains(keywd));
  return Json(new KendoGrid<SimMemberInfo>(request, result));
}

但有一個比較不符合我們使用的是,我們可能會從前端得表單內傳回許多查詢條件,因此會使用Model 來接變數 ,

並且宣告一個Property 名稱為「KendoGridRequest」 型態為「KendoGridRequest」,希望可以讓Kendo 直接ModelBinding 到這個變數上,

public class QueryModel
{
   public KendoGridRequest KendoGridRequest { get; set; }
   public string Condition1 { get; set; }
   public string Condition2 { get; set; }
   public string Condition3 { get; set; }
}

為了日後開發上的方便性與擴充性,我們不希望每一個開發人員在定義Model 時還要自行宣告這個變數,

所以我們這個變數就定義在BaseModel 上,這樣日後所有的ViewModel 只需要繼承這個Model 就可擁有所有的共用變數,

public class QueryModel:BaseModel
{
   public string Condition1 { get; set; }
   public string Condition2 { get; set; }
   public string Condition3 { get; set; }
}
public class BaseModel
{
   public KendoGridRequest KendoGridRequest { get; set; }
}

當然,也可以直接繼承「KendoGridRequest」,由於影響層面的關係,這次我們並沒有這麼做,

但也正因為如此,在GridDataSource 的部份我們必須要動一些手腳,不然Kendo 傳回來的資料,會接不到

另外為了共用及維護性,我另外建立了一個Kendo Widget ,

<div id="grid"></div>
<script type="text/javascript">
    $(function () {
        //建立Kendo DataSource
        var dataSrc = new kendo.data.DataSource({
            transport: {
                read: {
                    //以下其實就是$.ajax的參數
                    type: "POST",
                    url: "@Url.Action("GetGridData", "KendoExt")",
                    dataType: "json",
                    data: function () {
                    }
                }
            },
            pageSize: 10,
            serverPaging: true,
            serverSorting: true
        }
        );
        //這是Kendo Widget
        $('#grid').kendoExtGrid({
            dataSource: dataSrc,
            columns: [
                { field: "Id", title: "ID" },
                {
                    field: "Name", title: "Name",
                }
            ]
        });
    })
</script>

上面這段Code 沒有什麼特別的,就是宣告DataSource 接著做Kendo Widget 的初始化,

比較特別得是下面的一段Kendo Widget  Code,我們必須要在parameterMap 的地方 把回傳的變數 KendoGridRequest 指為空的,

這個作法其實是為了讓資料拋回後端時,去觸發Model Binding,提醒系統記得幫我們把資料塞進這個變數裡面。

$(function () {
	kendoui = kendo.ui,
	Widget = kendoui.Widget

	var ExtGrid = Widget.extend({
		dataSource: null,
		pageable: true,
		sortable: {
			mode: "multiple",
			allowUnsort: true
		},
		dataBound: function () { },
		init: function (element, options) {
			var that = this;
			Widget.fn.init.call(that, element, options);
			if (options.sortable) {
				sortable = options.sortable;
			}
			if (typeof options.dataBound === "function") {
				dataBound = options.dataBound;
			}

			if (!$.isEmptyObject(options.dataSource)) {
				//因為Schema 無法後來再指給他,只好重新new 一個DataSource起來重塞.
				//這個做法式為了讓共用更好操作,使用者無需再自行設定Schema and 設定
				//trigger KendoGridRequest Model Binding
				that.dataSource = new kendo.data.DataSource({
					schema: {
						//取出資料陣列
						data: function (d) { return d.data; },
						//取出資料總筆數(計算頁數用)
						total: function (d) { return d.total; }
					},
					pageSize: options.dataSource.options.pageSize,
					serverPaging: options.dataSource.options.serverPaging,
					serverSorting: options.dataSource.options.serverSorting

				});
				that.dataSource.transport = options.dataSource.transport;
				that.dataSource.transport.parameterMap = function (data, type) {
					if (type == "read") {
						//trigger KendoGridRequest Model Binding
						data.KendoGridRequest = null;
						return data;
					}
				};
			}

			this._initWidget(element, that, options);
		},
		_initWidget: function (element, that, options) {
			$('#' + element.id).kendoGrid({
				pageable: that.pageable,
				columns: options.columns,
				dataSource: that.dataSource,
				sortable: that.sortable,
				dataBound: function () {
					that.dataBound();
					//沒資料塞一列顯示"查無資料"
					var grid = $("#" + element.id).data("kendoGrid");
					if (grid.dataSource.view().length == 0) {
						var colCount = that.options.columns.length;
						$("#" + element.id).find('.k-grid-content tbody').append('<tr class="kendo-data-row"><td colspan="' +
                                colCount +
                                '" style="text-align:center"><b>查無資料!</b></td></tr>');
					}
				}
			});
		},
		options: {
			name: "ExtGrid"
		}
	});
	kendoui.plugin(ExtGrid);

})

透過上述的方法,我們節省了大量客製化的Code ,直接讓Kendo 幫我們完成許多事情!

GitHub:

https://github.com/changyuhao625/KendoExtension