公司產品使用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」