可编辑表格使用经验分享(持续更新)
对于Easyui的可编辑表格,个人也是较为陌生的,尽管在操作方式上可能比使用表单修改的方式便捷,但是可编辑表格对代码质量的要求往往更高一些,不熟练的话,容易出现这样或者那样的问题,本篇文章就自己使用的经历做一些总结。
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的WdatePicker.js文件,然后仿照text编辑器写my97日期型编辑器:
$.extend($.fn.datagrid.defaults.editors, { my97 : { init : function(container, options) { var input = $('<input class="Wdate" type="text" οnclick="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的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; }注意:因为动态改变编辑器类型需要在所有行都退出可编辑状态时才是安全的,所以这种方式只适合单行编辑模式。
这个地方我们要临时改变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之后,原始数据便是彻底消失,再也找不回来了。 目前就总结这么多,后面再有的话会陆续补上,本文提到的所有功能均在演示页面中可以找到。