现在的位置: 首页 > easyui > Grid > datagrid > 正文
可编辑表格使用经验分享(持续更新)
2012年11月10日 datagrid ⁄ 共 10661字 评论数 69 ⁄ 被围观 67,667 views+
文章目录
[隐藏]

对于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还提供了灵活的扩展接口,用户可以方便地自己扩展编辑器,比如官方提到的一个例子:

  1. $.extend($.fn.datagrid.defaults.editors, {
  2.     text: {
  3.         init: function(container, options){
  4.             var input = $('<input type="text" class="datagrid-editable-input">').appendTo(container);
  5.             return input;
  6.         },
  7.         getValue: function(target){
  8.             return $(target).val();
  9.         },
  10.         setValue: function(target, value){
  11.             $(target).val(value);
  12.         },
  13.         resize: function(target, width){
  14.             var input = $(target);
  15.             if ($.boxModel == true){
  16.                 input.width(width - (input.outerWidth() - input.width()));
  17.             } else {
  18.                 input.width(width);
  19.             }
  20.         }
  21.     }
  22. });
基于my97的编辑器

这里提别提一下将my97日期控件整合到可编辑表格的方法,只要扩展编辑器类型就可以了,当然了网页首先要引入my97的WdatePicker.js文件,然后仿照text编辑器写my97日期型编辑器:

  1. $.extend($.fn.datagrid.defaults.editors, {
  2.     my97 : {
  3.         init : function(container, options) {
  4.             var input = $('<input class="Wdate" type="text" onclick="WdatePicker({dateFmt:\'yyyy-MM-dd HH:mm:ss\',readOnly:true});"  />')
  5.                     .appendTo(container);
  6.             return input;
  7.         },
  8.         getValue : function(target) {
  9.             return $(target).val();
  10.         },
  11.         setValue : function(target, value) {
  12.             $(target).val(value);
  13.         },
  14.         resize : function(target, width) {
  15.             var input = $(target);
  16.             if ($.boxModel == true) {
  17.                 input.width(width - (input.outerWidth() - input.width()));
  18.             } else {
  19.                 input.width(width);
  20.             }
  21.         }
  22.     }
  23. });

为什么要将my97整合到可编辑表格中?无非是垂涎它的强大功能,特别是时间限制,方便的格式化定义等。而对于时间限制,好像仅仅靠扩展这个编辑器还远远不够,比如说我两个字段,一个是开始日期,一个结束日期,两个字段彼此是有约束关系的,我们该如何处理?这个后面会进一步给出解决方案。

简单的密码编辑器

请参见:http://www.easyui.info/archives/646.html

动态增加/删除编辑器

这两个方法原文出自夏悸的http://easyui.btboys.com/post-83.html,我在此基础上稍微做了修改,主要是将方法体放到each里面了,从而支持多个grid一起操作。

  1. $.extend($.fn.datagrid.methods, {
  2.     addEditor : function(jq, param) {
  3.         return jq.each(function(){
  4.             if (param instanceof Array) {
  5.                 $.each(param, function(index, item) {
  6.                     var e = $(jq).datagrid('getColumnOption', item.field);
  7.                     e.editor = item.editor;
  8.                 });
  9.             } else {
  10.                 var e = $(jq).datagrid('getColumnOption', param.field);
  11.                 e.editor = param.editor;
  12.             }
  13.         });
  14.     },
  15.     removeEditor : function(jq, param) {
  16.         return jq.each(function(){
  17.             if (param instanceof Array) {
  18.                 $.each(param, function(index, item) {
  19.                     var e = $(jq).datagrid('getColumnOption', item);
  20.                     e.editor = {};
  21.                 });
  22.             } else {
  23.                 var e = $(jq).datagrid('getColumnOption', param);
  24.                 e.editor = {};
  25.             }
  26.         });
  27.     }
  28. });

这两个扩展适合动态控制某列是否可编辑以及该列的编辑器类型,但是使用的时候要特别注意:当前表格还有处于编辑状态行的时候不要用这两个方法,会造成getEditor方法获取数据不准确,所以这两个扩展方法较为适合单行编辑模式

字段的级联操作

combobox的级联操作

那例子来分析,比如行政区域分级别,南京市下面有江宁区,栖霞区等;而连云港市下面有灌云县等。当我们选择不同城市的时候,区县级别的行政区域combobox内容要动态变化,这就是级联。

combobox要想实现级联,方式其实很简单,利用combobox的onSelect和onShowPanel事件就可以较为轻松地实现。onSelect事件用于我们例子提到的市级行政区域,而onShowPanel使用用于我们提到的区县级行政区域。两者都是为了动态加载区县级区域的内容。例如:

  1. <th rowspan="2" data-options="field:'city',width:100,align:'center',formatter:regionFormatter,
  2.     editor:{
  3.         type:'combobox',
  4.         options:{
  5.             valueField:'id',
  6.             textField:'name',
  7.             data:getRegions(''),
  8.             required:true,
  9.             onSelect:function(record){
  10.                 var target = $('#tt').datagrid('getEditor',{'index':editingIndex,'field':'county'}).target;
  11.                 target.combobox('clear');
  12.                 target.combobox('loadData',getRegions(record.id));
  13.                 target.combobox('setValue',getRegions(record.id)[0].id);
  14.             }
  15.         }
  16. }">城市</th>
  17. <th rowspan="2" data-options="field:'county',width:100,align:'center',formatter:regionFormatter,
  18.     editor:{
  19.         type:'combobox',
  20.         options:{
  21.             valueField:'id',
  22.             textField:'name',
  23.             data:regions,
  24.             required:true,
  25.             onShowPanel:function(){
  26.                 var targetCity = $('#tt').datagrid('getEditor',{'index':editingIndex,'field':'city'}).target;
  27.                 var targetCounty = $('#tt').datagrid('getEditor',{'index':editingIndex,'field':'county'}).target;
  28.                 var valueCity = targetCity.combobox('getValue');
  29.                 var valueCounty = targetCounty.combobox('getValue');
  30.                 targetCounty.combobox('clear');
  31.                 targetCounty.combobox('loadData',getRegions(valueCity));
  32.                 targetCounty.combobox('setValue',valueCounty);
  33.             }
  34.         }
  35. }">区县</th>

需要注意的是,代码中我们大量使用了getEditor方法,这个方法其实才是联动的纽带,通过它,我们可以实现编辑器之间交互的载体,通过它,我们可以定位到任意一个存在的编辑器,进而对其进行必要的操作。

文本类型编辑器的级联

类似文本类型编辑器主要是text,textarea,numberbox,validatebox,datebox这些编辑器。这个地方,我们拿前面提到的例子,日期框,而是使用我们扩展的my97编辑器,我们利用它来实现两个日期间的约束。

首先最重要的还是getEditor方法,它负责不同编辑器之间交互的载体;再者,对于文本,我们要自己绑定事件去实现对其它编辑器的约束,而且绑定的事情最好是有自己的命名空间,防止跟datagrid自带的事件冲突,这地方我们使用click.myNameSpace。直接看实现日期框联动的代码可能更为清晰:

  1. onClickRow:function (rowIndex) {
  2.     editingIndex = rowIndex;
  3.     if (lastIndex != rowIndex) {
  4.         if ($(this).datagrid('validateRow', lastIndex)) {
  5.             $(this).datagrid('endEdit', lastIndex);
  6.             $(this).datagrid('beginEdit', rowIndex);
  7.             var startTimeEditor = $('#tt').datagrid('getEditor', {
  8.                         index : rowIndex,
  9.                         field : "startTime"
  10.                     });
  11.             var endTimeEditor = $('#tt').datagrid('getEditor', {
  12.                         index : rowIndex,
  13.                         field : "endTime"
  14.                     });
  15.             if (startTimeEditor) {
  16.                 startTimeEditor.target.attr("onclick""");
  17.                 startTimeEditor.target.unbind("click.myNameSpace").bind(
  18.                         "click.myNameSpace"function(e) {
  19.                             var initObj = {
  20.                                 dateFmt : 'yyyy-MM-dd',
  21.                                 readOnly : false
  22.                             };
  23.                             if (endTimeEditor.target.val() != "")
  24.                                 initObj["maxDate"] = endTimeEditor.target.val();
  25.                             WdatePicker(initObj);
  26.                         });
  27.             }
  28.             if (endTimeEditor) {
  29.                 endTimeEditor.target.attr("onclick""");
  30.                 endTimeEditor.target.unbind("click.myNameSpace").bind(
  31.                         "click.myNameSpace"function(e) {
  32.                             var initObj = {
  33.                                 dateFmt : 'yyyy-MM-dd',
  34.                                 readOnly : false
  35.                             };
  36.                             if (startTimeEditor.target.val() != "")
  37.                                 initObj["minDate"] = startTimeEditor.target
  38.                                         .val();
  39.                             WdatePicker(initObj);
  40.                         });
  41.             }
  42.             lastIndex = rowIndex;
  43.         } else {
  44.             $(this).datagrid('selectRow', lastIndex);
  45.         }
  46.     }
  47. }

我们首先使用startTimeEditor.target.attr("onclick", "");将这种事情绑定方式删除,然后利用jQuery的事件机制做绑定,可以看到代码里面其实是对my97编辑器进行了重构,从而实现my97日期框可选日期范围的限制。

编辑字段对非编辑字段的依赖

有时候可编辑字段对不可编辑字段也有依赖关系。一个典型的场景,如饭店结账表格,有“应收金额(自动计算,不可编辑)”字段和“实收金额(收银员手工填写,可编辑)”字段,这种情况下,实收金额显然是不能大于应收金额的。

其实这种情况的处理比两个可编辑字段的级联更为简单,利用前面提到的“动态增加/删除编辑器”扩展,我们只要在onClickRow事件中动态设置“实收金额”字段的编辑器类型就可以了。

假设“实收金额”字段的编辑器类型是validatebox,使用自己扩展的max最大值规则,我们只要动态设置最大值就行了,如下代码仅供参考:

  1. onClickRow:function(rowIndex){
  2.     if (lastIndex != rowIndex){
  3.         $('#tt').datagrid('endEdit', lastIndex);
  4.         //获取当前行的应收金额值作为新的校验规则
  5.         var newMax = 'max[' + $('#tt').datagrid('getSelected').receivable + ']';
  6.         //动态改变实收金额字段的编辑器类型(只有在整个表格都没有处于编辑状态的行时改变编辑器类型才是安全的)
  7.         $('#tt').datagrid('addEditor ',{field:'paid',editor:{type:'validatebox',options:{validType:newMax}}});
  8.         $('#tt').datagrid('beginEdit', rowIndex);
  9.     }
  10.     lastIndex = rowIndex;
  11. }

注意:因为动态改变编辑器类型需要在所有行都退出可编辑状态时才是安全的,所以这种方式只适合单行编辑模式。

数据提交与恢复

利用loading提高用户体验

这个地方我们要临时改变loadMsg,因为默认的提示是“正在加载中……”,我们提交数据的时候应改为“正在保存中……”,保存成功后再还原loadMsg属性,例如:

  1. if ($.isEmptyObject(chanages) == false) {
  2.     var bakMsg = $(this).datagrid('options').loadMsg;
  3.     $(this).datagrid('options').loadMsg = "正在保存中……";
  4.     $('#tt').datagrid('loading');
  5.     setTimeout(function() {
  6.                 $('#tt').datagrid('loaded');
  7.                 $('#tt').datagrid('options').loadMsg = bakMsg;
  8.             }, 1000);
  9. }
结束编辑后获取原始数据

在调用acceptChanges之前,表格的原始行数据都可以通过绑定到DOM上的对象的originalRows属性获取,即:

  1. 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

目前有 69 条留言 其中:访客:39 条, 博主:30 条

  1. yokoboy : 2013年02月15日13:36:49  -49楼 @回复 回复
  2. tang2992 : 2013年04月10日20:03:14  -48楼 @回复 回复
  3. checke : 2013年07月10日19:09:32  -47楼 @回复 回复
  4. luolibing : 2013年08月08日22:51:40  -46楼 @回复 回复
  5. checkme : 2013年08月12日16:33:26  -45楼 @回复 回复
  6. it新人 : 2013年09月13日10:55:34  -44楼 @回复 回复
  7. it新人 : 2013年09月14日10:41:44  -43楼 @回复 回复
  8. 小五 : 2013年09月29日15:22:46  -42楼 @回复 回复
  9. xiaocai : 2013年11月10日14:56:32  -41楼 @回复 回复
  10. easyui菜鸟 : 2013年11月20日15:48:00  -40楼 @回复 回复
  11. allan : 2014年02月26日17:55:54  -39楼 @回复 回复
  12. 小五 : 2014年03月05日18:27:07  -38楼 @回复 回复
  13. 苗文正 : 2014年04月11日02:14:47  -37楼 @回复 回复
  14. 小伙 : 2014年05月06日16:59:16  -36楼 @回复 回复
  15. 黑颈 : 2014年05月15日22:22:26  -35楼 @回复 回复
  16. hailiang : 2014年07月16日17:52:31  -34楼 @回复 回复
  17. sofine : 2014年07月24日16:02:29  -33楼 @回复 回复
  18. web开发分享 : 2015年02月04日12:15:16  -32楼 @回复 回复
  19. JuneDing : 2015年02月05日18:01:29  -31楼 @回复 回复
  20. JuneDing : 2015年02月05日18:08:41  -30楼 @回复 回复
  21. S : 2015年02月27日11:10:27  -29楼 @回复 回复
  22. 三五闲心 : 2015年04月16日14:22:18  -28楼 @回复 回复
  23. 不锈钢网 : 2015年07月20日14:26:54  -27楼 @回复 回复
  24. 水中鳥 : 2015年08月12日23:07:28  -26楼 @回复 回复
  25. lonelyman : 2015年08月13日10:33:33  -25楼 @回复 回复

给我留言

留言无头像?


×