easyui datagrid可編輯表格使用經驗分享
對於Easyui的可編輯表格,個人也是較為陌生的,儘管在操作方式上可能比使用表單修改的方式便捷,但是可編輯表格對程式碼質量的要求往往更高一些,不熟練的話,容易出現這樣或者那樣的問題,本篇文章就自己使用的經歷做一些總結。
相關介面方法
Name | Parameter | Description |
---|---|---|
Method: | ||
beginEdit | index | 使行進入可編輯狀態 |
endEdit | index | 結束行的可編輯狀態,所做的改動將會被臨時儲存 |
cancelEdit | index | 取消編輯狀態,所做的改動將會還原。 |
getChanges | type | 獲取被更改的行資料,返回一個物件陣列,每個陣列元素的內容是對應行被修改的欄位鍵值;需要注意的是在使用acceptChanges方法之後便會清空所有變更資料,這時getChanges是獲取不到資料的。入參為type,包含以下三個值: inserted: 只獲取插入的行資料; deleted: 只獲取被刪除的行資料; updated: 只獲取欄位有更新的行資料; 如果不設定type,將會獲取所有變化的行資料 |
acceptChanges | null | 提交所有被更改的資料行,提交之後就無法使用rejectChanges方法對所作的修改做回滾操作了。 |
rejectChanges | none | 回滾所有的操作,還原為表格的最初資料,在acceptChanges之後便無法再回滾了。 |
validateRow | index | 對行資料進行校驗,全部欄位都校驗通過才返回true,入參為行的索引. |
getEditors | index | 獲取某一行的所有編輯器,注意該方法必須在beginEdit方法執行之後,endEdit方法執行之前才能獲取到編輯器,即,只有編輯狀態的行才能獲取到編輯器.返回為物件陣列,每個元素包含以下資訊: actions: the actions that the editor can do, same as the editor definition. target: 編輯器對應DOM的jQuery物件. field: 欄位的field. type: 編輯器型別, 比如 'text','combobox','datebox', 等 |
getEditor | options | 獲取某一行某一列的編輯器,注意該方法必須在beginEdit方法執行之後,endEdit方法執行之前才能獲取到編輯器,即,只有編輯狀態的行才能獲取到編輯器.返回值為物件,物件資訊同於getEditors。入參包含: index: 行索引. field: 欄位的field. |
addEditor | params | 增加某列的編輯器,params是物件,包含field屬性以及editor屬性,其中editor物件包含type和編輯器options屬性;params也可以是一陣列,用於同時操作多列. |
removeEditor | params | 刪除某列的編輯器,params可以為某列的field或者是包含多個field的陣列. |
Event: | ||
onClickRow | rowIndex, rowData | 行點選事件,通常在該事件裡面觸發行的可編輯狀態,使當前行可以編輯. |
onBeforeEdit | rowIndex, rowData | 當行進入可編輯狀態的時候觸發該事件. |
onAfterEdit | rowIndex, rowData, changes | 當結束可編輯狀態時觸發該事件,changes為物件,記錄了發生變化的欄位以及對應的值. |
onCancelEdit | rowIndex, rowData | 當取消行編輯的時候呼叫該事件. |
列屬性formatter
formatter屬性對於可編輯表格來講是非常重要的,想combobox,checkbox這些編輯器,如果不用formatter進行格式化的話,最終datagrid會顯示我們的key而不是我們想要的desc,所以formatter屬性非常重要。
編輯器型別
根據Easyui datagrid的設計,每一列可以對應一種編輯器型別,比如說校驗框,下拉框等。框架自帶了以下幾種編輯器:
text,textarea,checkbox,numberbox,validatebox,datebox,combobox,combotree
除了框架自帶的編輯器,datagrid還提供了靈活的擴充套件介面,使用者可以方便地自己擴充套件編輯器,比如官方提到的一個例子:
- $.extend($.fn.datagrid.defaults.editors, {
- text: {
- init: function(container, options){
- var input = $('<input type="text" class="datagrid-editable-input">').appendTo(container);
- return input;
- },
- getValue: function(target){
- return $(target).val();
- },
- setValue: function(target, value){
- $(target).val(value);
- },
- resize: function(target, width){
- var input = $(target);
- if ($.boxModel == true){
- input.width(width - (input.outerWidth() - input.width()));
- } else {
- input.width(width);
- }
- }
- }
- });
基於my97的編輯器
這裡提別提一下將my97日期控制元件整合到可編輯表格的方法,只要擴充套件編輯器型別就可以了,當然了網頁首先要引入my97的WdatePicker.js檔案,然後仿照text編輯器寫my97日期型編輯器:
- $.extend($.fn.datagrid.defaults.editors, {
- my97 : {
- init : function(container, options) {
- var input = $('<input class="Wdate" type="text" onclick="WdatePicker({dateFmt:\'yyyy-MM-dd HH:mm:ss\',readOnly:true});" />')
- .appendTo(container);
- return input;
- },
- getValue : function(target) {
- return $(target).val();
- },
- setValue : function(target, value) {
- $(target).val(value);
- },
- resize : function(target, width) {
- var input = $(target);
- if ($.boxModel == true) {
- input.width(width - (input.outerWidth() - input.width()));
- } else {
- input.width(width);
- }
- }
- }
- });
為什麼要將my97整合到可編輯表格中?無非是垂涎它的強大功能,特別是時間限制,方便的格式化定義等。而對於時間限制,好像僅僅靠擴充套件這個編輯器還遠遠不夠,比如說我兩個欄位,一個是開始日期,一個結束日期,兩個欄位彼此是有約束關係的,我們該如何處理?這個後面會進一步給出解決方案。
簡單的密碼編輯器
請參見:http://www.easyui.info/archives/646.html
動態增加/刪除編輯器
這兩個方法原文出自夏悸的http://easyui.btboys.com/post-83.html,我在此基礎上稍微做了修改,主要是將方法體放到each裡面了,從而支援多個grid一起操作。
- $.extend($.fn.datagrid.methods, {
- addEditor : function(jq, param) {
- return jq.each(function(){
- if (param instanceof Array) {
- $.each(param, function(index, item) {
- var e = $(jq).datagrid('getColumnOption', item.field);
- e.editor = item.editor;
- });
- } else {
- var e = $(jq).datagrid('getColumnOption', param.field);
- e.editor = param.editor;
- }
- });
- },
- removeEditor : function(jq, param) {
- return jq.each(function(){
- if (param instanceof Array) {
- $.each(param, function(index, item) {
- var e = $(jq).datagrid('getColumnOption', item);
- e.editor = {};
- });
- } else {
- var e = $(jq).datagrid('getColumnOption', param);
- e.editor = {};
- }
- });
- }
- });
這兩個擴充套件適合動態控制某列是否可編輯以及該列的編輯器型別,但是使用的時候要特別注意:當前表格還有處於編輯狀態行的時候不要用這兩個方法,會造成getEditor方法獲取資料不準確,所以這兩個擴充套件方法較為適合單行編輯模式。
欄位的級聯操作
combobox的級聯操作
那例子來分析,比如行政區域分級別,南京市下面有江寧區,棲霞區等;而連雲港市下面有灌雲縣等。當我們選擇不同城市的時候,區縣級別的行政區域combobox內容要動態變化,這就是級聯。
combobox要想實現級聯,方式其實很簡單,利用combobox的onSelect和onShowPanel事件就可以較為輕鬆地實現。onSelect事件用於我們例子提到的市級行政區域,而onShowPanel使用用於我們提到的區縣級行政區域。兩者都是為了動態載入區縣級區域的內容。例如:
- <th rowspan="2" data-options="field:'city',width:100,align:'center',formatter:regionFormatter,
- editor:{
- type:'combobox',
- options:{
- valueField:'id',
- textField:'name',
- data:getRegions(''),
- required:true,
- onSelect:function(record){
- var target = $('#tt').datagrid('getEditor',{'index':editingIndex,'field':'county'}).target;
- target.combobox('clear');
- target.combobox('loadData',getRegions(record.id));
- target.combobox('setValue',getRegions(record.id)[0].id);
- }
- }
- }">城市</th>
- <th rowspan="2" data-options="field:'county',width:100,align:'center',formatter:regionFormatter,
- editor:{
- type:'combobox',
- options:{
- valueField:'id',
- textField:'name',
- data:regions,
- required:true,
- onShowPanel:function(){
- var targetCity = $('#tt').datagrid('getEditor',{'index':editingIndex,'field':'city'}).target;
- var targetCounty = $('#tt').datagrid('getEditor',{'index':editingIndex,'field':'county'}).target;
- var valueCity = targetCity.combobox('getValue');
- var valueCounty = targetCounty.combobox('getValue');
- targetCounty.combobox('clear');
- targetCounty.combobox('loadData',getRegions(valueCity));
- targetCounty.combobox('setValue',valueCounty);
- }
- }
- }">區縣</th>
需要注意的是,程式碼中我們大量使用了getEditor方法,這個方法其實才是聯動的紐帶,通過它,我們可以實現編輯器之間互動的載體,通過它,我們可以定位到任意一個存在的編輯器,進而對其進行必要的操作。
文字型別編輯器的級聯
類似文字型別編輯器主要是text,textarea,numberbox,validatebox,datebox這些編輯器。這個地方,我們拿前面提到的例子,日期框,而是使用我們擴充套件的my97編輯器,我們利用它來實現兩個日期間的約束。
首先最重要的還是getEditor方法,它負責不同編輯器之間互動的載體;再者,對於文字,我們要自己繫結事件去實現對其它編輯器的約束,而且繫結的事情最好是有自己的名稱空間,防止跟datagrid自帶的事件衝突,這地方我們使用click.myNameSpace。直接看實現日期框聯動的程式碼可能更為清晰:
- onClickRow:function (rowIndex) {
- editingIndex = rowIndex;
- if (lastIndex != rowIndex) {
- if ($(this).datagrid('validateRow', lastIndex)) {
- $(this).datagrid('endEdit', lastIndex);
- $(this).datagrid('beginEdit', rowIndex);
- var startTimeEditor = $('#tt').datagrid('getEditor', {
- index : rowIndex,
- field : "startTime"
- });
- var endTimeEditor = $('#tt').datagrid('getEditor', {
- index : rowIndex,
- field : "endTime"
- });
- if (startTimeEditor) {
- startTimeEditor.target.attr("onclick", "");
- startTimeEditor.target.unbind("click.myNameSpace").bind(
- "click.myNameSpace", function(e) {
- var initObj = {
- dateFmt : 'yyyy-MM-dd',
- readOnly : false
- };
- if (endTimeEditor.target.val() != "")
- initObj["maxDate"] = endTimeEditor.target.val();
- WdatePicker(initObj);
- });
- }
- if (endTimeEditor) {
- endTimeEditor.target.attr("onclick", "");
- endTimeEditor.target.unbind("click.myNameSpace").bind(
- "click.myNameSpace", function(e) {
- var initObj = {
- dateFmt : 'yyyy-MM-dd',
- readOnly : false
- };
- if (startTimeEditor.target.val() != "")
- initObj["minDate"] = startTimeEditor.target
- .val();
- WdatePicker(initObj);
- });
- }
- lastIndex = rowIndex;
- } else {
- $(this).datagrid('selectRow', lastIndex);
- }
- }
- }
我們首先使用startTimeEditor.target.attr("onclick", "");將這種事情繫結方式刪除,然後利用jQuery的事件機制做繫結,可以看到程式碼裡面其實是對my97編輯器進行了重構,從而實現my97日期框可選日期範圍的限制。
編輯欄位對非編輯欄位的依賴
有時候可編輯欄位對不可編輯欄位也有依賴關係。一個典型的場景,如飯店結賬表格,有“應收金額(自動計算,不可編輯)”欄位和“實收金額(收銀員手工填寫,可編輯)”欄位,這種情況下,實收金額顯然是不能大於應收金額的。
其實這種情況的處理比兩個可編輯欄位的級聯更為簡單,利用前面提到的“動態增加/刪除編輯器”擴充套件,我們只要在onClickRow事件中動態設定“實收金額”欄位的編輯器型別就可以了。
假設“實收金額”欄位的編輯器型別是validatebox,使用自己擴充套件的max最大值規則,我們只要動態設定最大值就行了,如下程式碼僅供參考:
- onClickRow:function(rowIndex){
- if (lastIndex != rowIndex){
- $('#tt').datagrid('endEdit', lastIndex);
- //獲取當前行的應收金額值作為新的校驗規則
- var newMax = 'max[' + $('#tt').datagrid('getSelected').receivable + ']';
- //動態改變實收金額欄位的編輯器型別(只有在整個表格都沒有處於編輯狀態的行時改變編輯器型別才是安全的)
- $('#tt').datagrid('addEditor ',{field:'paid',editor:{type:'validatebox',options:{validType:newMax}}});
- $('#tt').datagrid('beginEdit', rowIndex);
- }
- lastIndex = rowIndex;
- }
注意:因為動態改變編輯器型別需要在所有行都退出可編輯狀態時才是安全的,所以這種方式只適合單行編輯模式。
資料提交與恢復
利用loading提高使用者體驗
這個地方我們要臨時改變loadMsg,因為預設的提示是“正在載入中……”,我們提交資料的時候應改為“正在儲存中……”,儲存成功後再還原loadMsg屬性,例如:
- if ($.isEmptyObject(chanages) == false) {
- var bakMsg = $(this).datagrid('options').loadMsg;
- $(this).datagrid('options').loadMsg = "正在儲存中……";
- $('#tt').datagrid('loading');
- setTimeout(function() {
- $('#tt').datagrid('loaded');
- $('#tt').datagrid('options').loadMsg = bakMsg;
- }, 1000);
- }
結束編輯後獲取原始資料
在呼叫acceptChanges之前,表格的原始行資料都可以通過繫結到DOM上的物件的originalRows屬性獲取,即:
- var originalRows = $('#tt').data('datagrid').originalRows;
而在呼叫acceptChanges之後,原始資料便是徹底消失,再也找不回來了。
目前就總結這麼多,後面再有的話會陸續補上,本文提到的所有功能均在演示頁面中可以找到。
常見問題集錦
表頭和資料表格錯位
錯位場景一:資料只有一行;且處於可編輯狀態;且datagrid的初始化方式是javascript;且初始化datagrid的指令碼沒有放在document.ready裡面,而是放到了body的閉合標籤前。使勁點下面連結看演示:
http://easyui.info/version/jquery-easyui-1.3.5/demo/datagrid/dislocation_01.html
解決方式:老老實實的把初始化datagrid的指令碼寫到document.ready裡面。
效果演示
單行編輯:http://www.easyui.info/easyui/demo/datagrid/063.html
多行編輯:http://www.easyui.info/easyui/demo/datagrid/064.html