專案中使用Kendo UI Grid控制元件實現批量編輯,現在將用到的功能總結一下。
批量編輯基本設定
Kendo Grid的設定方法如下:
$("#grid").kendoGrid({
toolbar: ["create","save", "cancel"],
columns: [
{ field: "OBJECTID", title: "ID" },
...
],
dataSource: {
transport: {
read: {
url: myRoot + "GetPagedData"
},
update: {
url: myRoot + "UpdateBatch"
},
create: {
url: myRoot + "UpdateBatch"
},
delete:{
url:myRoot+"DeleteDate"
}
},
pageSize: 100,
serverPaging: true,
serverSorting: true,
schema: {
total: "rowCount",
data: "Rows",
model: {
id: "OBJECTID",
fields: {
OBJECTID: { editable: false, nullable: true },
NAME: { editable: true, validation: { required: true } },
...
}
}
},
batch: true,
},
pageable: {
pageSize: 10
},
height: "600px",
editable: true
});
- toolbar:建立網格中的按鈕,這裡建立“新增”、“儲存”和“取消”。
- columns:在網格中顯示的列,這是一個陣列,每一列可以定義欄位的名稱,顯示的文字,顯示的模板和自定義的編輯器
- dataSource:定義了與網格相關的kendo dataSource物件。在transport中定義了進行CRUD的物件(或者函式),如果批量修改,需要在dataSource中將batch設定為true。如果批量修改,還需要在schema中設定model,包括id和各個欄位的定義。
- editable屬性需要設定為true。
基本設定完成後,網格就可以進行批量編輯了,點選網格中的單元格,會載入動態文字框,進行編輯,編輯完成後,改變值的網格會有變化提示。
Grid批量編輯中的下拉框設定
在前面的例子中,網格中顯示人員所在的部門,我們在編輯時,需要使用下拉框選擇所在的部門:
這裡需要解決幾個問題:
1、我們在資料庫中儲存的是部門的id,不是部門的名稱,而在顯示時,需要顯示部門的名稱。
2、在編輯時,我們需要載入下拉框,並獲取部門的資料,進行編輯。
為了解決第一個問題,我們在返回的資料中,不僅返回部門的id,還返回部門的名稱,
model: {
id: "OBJECTID",
fields: {
...
DEPARTMENTID: { editable: true, },
DEPARTMENTID_DicText: { editable: false, nullable: true },
...
}
}
在模型定義中,定義DEPARTMENTID為可編輯的,DEPARTMENTID_DicText不可編輯。
在columns的定義中,定義DEPARTMENTID的顯示模板為DEPARTMENTID_DicText:
{ field: "DEPARTMENTID", title: "部門", editor: categoryDropDownEditor, template: "#=DEPARTMENTID_DicText#" },
這樣DEPARTMENTID在網格顯示時,實際顯示的是DEPARTMENTID_DicText
為了實現下拉框選擇,我們建立自定義的編輯器,categoryDropDownEditor,這個編輯器是一個函式:
//自定義編輯控制元件,下拉框
function categoryDropDownEditor(container, options) {
$('<input name="' + options.field + '"/>')
.appendTo(container)
.kendoDropDownList({
autoBind: false,
dataTextField: "Text",
dataValueField: "Value",
height: 600,
dataSource: {
transport: {
read: {
url: myRoot + "GetDictionary"
}
}
},
change: function (e) {
options.model[options.field + "_DicText"] = e.sender.text();
}
});
}
這個函式中建立了一個input控制元件,並轉換為kendoDropDownList下拉框,下拉框通過ajax獲取部門的資料,選擇後,修改模型中儲存的資料。
Grid批量編輯中的日期編輯器設定
前面我們討論了在Kendo UI Grid中下拉框的設定,這裡討論如何設定日期編輯器。
自定義日期編輯器
在上面的例子中,有入職日期欄位,需要使用日期選擇器進行編輯,由於kendo的日期型在伺服器端不容易轉換,所有我們採用字串進行傳輸,因此在模型中,我們沒有將這個欄位定義為日期型,而是保持為字元型:
model: {
id: "OBJECTID",
fields: {
...
JOINDATE: { editable: true, format: "yyyy-MM-dd", type: "string" },
...
}
}
在columns中:
columns: [
...
{ field: "JOINDATE", title: "入職日期", editor: dateEditor },
...
],
在這裡,我們定義了這個列的自定義編輯器,是一個函式,自定義的日期編輯器函式如下:
//自定義編輯控制元件,日期
function dateEditor(container, options) {
$('<input name="' + options.field + '"/>')
.appendTo(container)
.kendoDatePicker(
{
format: "yyyy-MM-dd",
culture: "zh-CN",
change: function (e) {
options.model[options.field] = kendo.toString(e.sender.value(), "yyyy-MM-dd");
}
})
}
在這個函式裡,建立一個input標籤,並轉換為kendo DatePicker,在change事件中改變模型中的值。這樣就完成了網格單元格編輯的日期編輯器。
Grid批量編輯中的Checkbox編輯器設定
我們需要在網格中用checkbox顯示ISFULLTIME,如果值為1,顯示選中,如果為0,顯示沒有選中。可以通過建立模板的方式實現。模板程式碼如下:
{ template: '#=dirtyField(data,"ISFULLTIME")#<input type="checkbox" #=ISFULLTIME== 1 ? \'checked="checked"\' : "" # class="chkbx" ref="ISFULLTIME" />', width: 110 },
我們在change事件中響應選擇動作,改變模型中相應的值。
$("#grid .k-grid-content").on("change", "input.chkbx", function (e) {
var field = $(this).attr("ref")
var grid = $("#grid").data("kendoGrid"),
dataItem = grid.dataItem($(e.target).closest("tr"));
dataItem.set(field, this.checked ? 1 : 0);
});
我們還需要處理髒資料,也就是正在編輯的資料,如果資料發生了改變,則在網格中進行標識。這個函式的程式碼如下:
function dirtyField(data, fieldName) {
var hasClass = $("[data-uid=" + data.uid + "]").find(".k-dirty-cell").length < 1;
if (data.dirty && data[fieldName]!=data[fieldName+"_DICTEXT"] && hasClass) {
return "<span class='k-dirty'></span>"
}
else {
return "";
}
}
Grid批量編輯中的Checkbox編輯器另一種方法
在這個例子中,有一個是否全職的欄位,ISFULLTIME,這個欄位是數值型,用1和0表示true和false,在編輯時,我們希望使用checkbox進行編輯,但希望獲得的值是1和0,而不是true和false。
在model中的定義這個欄位和一個附加欄位,用於編輯:
model: {
id: "OBJECTID",
fields: {
...
ISFULLTIME: { editable: true },
ISFULLTIME_DICTEXT: { editable: true}
}
在columns中的定義,注意這裡我們編輯的是ISFULLTIME_DICTEXT:
columns: [
...
{ field: "ISFULLTIME_DICTEXT", title: "是否全職", editor: customBoolEditor, template: "#=GetFullTime(ISFULLTIME)#" },
],
這裡我們希望顯示“是”和“否”,因此模板呼叫了轉換函式:
function GetFullTime(va) {
if (va == 1) return "是";
return "否";
}
自定義的編輯器如下:
//自定義編輯控制元件,checkbox
function customBoolEditor(container, options) {
var guid = kendo.guid();
$('<input class="k-checkbox" id="' + guid + '" type="checkbox" name="' + options.field + '" data-type="boolean" data-bind="checked:' + options.field + '">').appendTo(container)
.on("click", function (e) {
var field = options.field.replace("_DICTEXT", "");
if (e.target.checked)
options.model[field] = 1;
else
options.model[field] = 0;
});
$('<label class="k-checkbox-label" for="' + guid + '">​</label>').appendTo(container);
}
這裡,在控制元件值改變時,將模型相關的值改變為1或0。
在網格之外新增“新增”、“儲存”和“取消”
在網格中增加“新增”、“儲存”和“取消”很容易,只要在網格定義中增加toolbar: ["create","save", "cancel"]就可以了。但在某些情況下,我們希望在網格之外增加這些功能按鈕,並通過呼叫Api實現相應的功能。
如果我們希望在網格之外新增這幾個按鈕,可以直接呼叫dataSource的相關api:
html程式碼:
<span id="btnAdd" class="btn btn-default">新增</span>
<span id="btnSave" class="btn btn-default">儲存</span>
<span id="btnCancel" class="btn btn-default">取消修改</span>
相關的JS程式碼如下:
//增加儲存按鈕
$("#btnSave").bind("click", function () {
var grid = $("#grid").data("kendoGrid");
grid.dataSource.sync();;
});
//增加取消按鈕
$("#btnCancel").bind("click", function () {
var grid = $("#grid").data("kendoGrid");
grid.dataSource.cancelChanges();
});
//增加新增按鈕
$("#btnAdd").bind("click", function () {
var grid = $("#grid").data("kendoGrid");
grid.dataSource.add();
});
這些按鈕執行效果與網格中toolbar定義的按鈕效果相同。
為Kendo Grid 增加自定義統計行
Kendo Grid支援增加列統計的功能,對某一列的資料在底部顯示合計值、平均值等等。但這些功能都是在客戶端進行的,如果資料量很大,需要在服務端進行分頁處理等,就不能使用這些客戶端的功能,而是需要在服務端進行計算,然後在網格中顯示。為了實現這種需求,我們需要:
- 在服務端增加計算統計值的Api
- 在客戶端呼叫這個Api,使用自定義模板顯示
這裡假設我們已經有了在服務計算統計值的Api,主要說明如何在客戶端顯示這些計算值。
首先,需要增加儲存統計值的物件:
var agData = {};//儲存從伺服器Api獲取的統計資料
然後,在grid的列定義中增加footerTemplate,比如:
{ field: "AGE", title: "年齡", width: "500px", footerTemplate:"<span id='footerAGE'>#=getAgData('AGE')#</span>"},
在模板裡,我們使用一個函式獲取統計值,獲取函式如下:
function getAgData(field) {
if (agData[field]) return agData[field];
return "";
}
在查詢函式中呼叫服務端的Api,獲取統計資料,並將統計資料儲存到agData:
function doSearch() {
//網格查詢部分略
//這裡通過自定義的Api查詢統計資料,postdata中包含查詢條件
MyApi.getSumData(postdata, function (res) {
agData = res;
});
}
Grid保持行展開狀態
在使用Kendo UI Grid時,如果重新繫結,也就是當dataBound事件被觸發時,原來展開的行會收起,如果希望保持行的展開狀態,需要在行展開時,記錄展開行的id,然後在重新繫結時,回覆展開的狀態。我們可以將展開行的id保持在本地儲存中。下面的例子是使用MVVM下的程式碼:
<div id="dsgrid" class="grid"
data-role="grid"
data-sortable="true"
data-toolbar="['create']"
data-detail-init="viewModel.dsgrid_detailInit"
data-bind="source: dsSource, events: { dataBound: ds_dataBound,detailExpand:ds_DetailExpand, detailCollapse:ds_DetailCollapse }"
data-editable='{"mode": "popup" }'
data-columns='[
{"field":"Name","title":"名稱"},
{"field":"Title","title":"說明"},
{"field":"TableName","title":"資料庫資料來源表名稱"},
{"field":"GetDataUrl","title":"Api資料來源Url"},
{"field":"TextField","title":"文字欄位"},
{"field":"ValueField","title":"值欄位"},
{"command": [ "edit", "destroy" ], "filterable": false, "sortable": false, "width:": "240px"}
]'
data-detail-template='dssubgrid'
data-scrollable="false">
</div>
程式碼中宣告瞭三個事件,dataBound,detailExpand和detailCollapse,在detailExpand事件中,記錄下展開行對應的資料id:
ds_DetailExpand: function (e) {
var grid = $("#dsgrid").data("kendoGrid");
var item = grid.dataItem(e.masterRow);
var items = localStorage['expanded'];
if (items) {
items = JSON.parse(items);
} else {
items = [];
}
items.push(item.Name);
localStorage['expanded'] = JSON.stringify(items);
},
在detailCollapse事件中,將該行記錄的id去掉:
ds_DetailCollapse: function (e) {
var grid = $("#dsgrid").data("kendoGrid");
var item = grid.dataItem(e.masterRow);
var items = JSON.parse(localStorage['expanded']);
items = items.filter(function (x) {
return x != item.Name;
});
localStorage['expanded'] = JSON.stringify(items);
},
在dataBound事件中,展開對應的行:
ds_dataBound: function (e) {
var row = e.sender.tbody.find("tr.k-master-row").first();
var items = localStorage['expanded'];
var grid = $("#dsgrid").data("kendoGrid");
if (items) {
items = JSON.parse(items);
items.forEach(function (x) {
var item = grid.dataSource.view().find(function (y) {
return y.Name == x;
});
if (item) {
row = $('#' + grid.element.attr('id') + ' tr[data-uid="' + item.uid + '"]')
}
})
}
e.sender.expandRow(row);
},