论坛首页 Web前端技术论坛

也来说说jQuery的grid插件Flexigrid的几点重要改进

浏览 3108 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2011-02-10   最后修改:2011-02-10
之所以选择Flexigrid作为我们的grid,三个重要原因:
1.我们选择了jQuery作为基础js lib;
2.要能全面控盘,也就是说只要需要,可以在没有很强的js牛人参与的情况下,能自行扩充;
3.性能和browser兼容性要好

Ok,下面来谈谈我的几点关键改进(这里忽略参考来的,仅谈自己的扩展和改进,并且仅探讨几个关键改进,细小的就不谈了)

1.兼容复杂的JSON格式。
我们都知道,不可能所有的POJO都是完全扁平的,比如Booking类,里面的定义可能是这样的:
public class Booking implements Serializable {
	private Long id;
	private User user;
	private Hotel hotel;
	@DateTimeFormat(pattern = "MM-dd-yyyy")
	private Date checkinDate;
	@DateTimeFormat(pattern = "MM-dd-yyyy")
	private Date checkoutDate;
	private String creditCard;
	private String creditCardName;
	private int creditCardExpiryMonth;
	private int creditCardExpiryYear;
	private boolean smoking;
	private int beds;
...
}

出来的JSON就可能是这样的:
{"total":1,"page":1,"rows":[{"id":34,"total":300,"description":"70 Park Avenue Hotel, Feb 10, 2011 to Feb 11, 2011","user":{"name":"Keith","password":null,"username":"keith"},"checkinDate":1297267200000,"checkoutDate":1297353600000,"nights":1,"hotel":{"address":"70 Park Avenue","name":"70 Park Avenue Hotel","id":9,"state":"NY","country":"USA","price":300,"city":"NY","zip":"10011"},"creditCard":null,"smoking":false,"beds":0,"creditCardName":null,"creditCardExpiryMonth":0,"creditCardExpiryYear":0,"amenities":null}]}

注意这里的user和hotel都是一个子对象。
在这种情况下,Flexigrid并不能支持,原来的代码是这样的:
for (j = 0; j < p.colModel.length; j++) {
	var cm = p.colModel[j];
	var selectedName = cm.name;
	// properties set but not in data, reset to ''
	if (data.rows[i][selectedName] == null) {
		data.rows[i][selectedName] = '';
	}
	// push the current row data to array tdVal
	$.each(data.rows[i], function(name, value) {
	  if (selectedName == name) {
	  	if (cm.render) {
	  		val = cm.render(value);
		} else {
			val = value;
		}
		tdVal.push(val);
	  }
	});
}

大家可以看到,如果我们的colModel配置如下:
colModel : [
{display: 'id', name : 'id', width : 10, align: 'left'},
{display: 'Hotel Name', name : 'hotel.name', width : 150, sortable : true, align: 'left'},
{display: 'Hotel Address', name : 'hotel.address', width : 200, sortable : true, align: 'center'},
{display: 'Hotel City', name : 'hotel.city', width : 80, align: 'left'},
{display: 'Hotel State', name : 'hotel.state', width : 80, align: 'left'},
{display: 'Check in Date', name : 'checkinDate', width : 100, align: 'left'},
{display: 'Check out Date', name : 'checkoutDate', width : 100, align: 'right'},//sum
{display: 'Action', name : 'cancel', width : 50, align: 'center', render: renderCancelIt}//using custom render
]

这是无法得到正确的答案的,因为selectedName是hotel.name比如,但data.rows[i][selectedName]这种写法永远都是取不到hotel.name的值的,所以总是为null.
而且大家注意到,上面的js的效率非常差,两个嵌套循环简直是浪费。
改进其实很简单,代码也更少更精练,而且由于免去了多余的循环,性能得到大幅的提升。
代码如下:
for (j=0;j<p.colModel.length;j++){
	var cm = p.colModel[j];
	if(cm.summary) isSummary = true;
		//10/02/2011 enhanced by bright for support complex properties by rewrite this part
		//using eval for getting the value no matter how complex it is
		val = eval("data.rows[" + i +"]." + cm.name);
		if (val==null) val = "";
		if (cm.render) {
			//07/02/2011 by bright: let custom render more powerful
			//val = cm.render(value);
			val = cm.render(val, row);
		}
		tdVal.push(val);
	}
}

需要再提一下的是:我这里的render方法由于在第二个参数中加了current row的对象,所以非常方便我们在字段中引用其他属性值,这也是使得flexigrid变得更加可用的一点小改进。比如:
function getMoreInfo(content, currentRow){
	var str = "id=" + currentRow.id + ";name=" + currentRow.name;
	return '<input type="button" name="doit" value="More Info" onclick="alert(\'' + str + '\')">';
}



2.加入小计功能。需要说明的是,这里的统计仅对当前页进行简单的客户端的统计——如果要做更复杂的,建议在后端做,然后当一个普通的record来渲染。
首先加入一个插件范围的全局函数:
appendSummaryFooter: function(tbody){
	var data = [];
	for (j=0;j<p.colModel.length;j++){
		var cm = p.colModel[j];
		var summary = cm.summary;
		if(summary){
			var vals = []; 
			var trs = $(".flexigrid .bDiv table tbody tr"); 
			if(trs.length > 0){ 
				for(var x=0; x<trs.length; x++){
					var v = $("td:eq("+j+")",trs[x]).text();
					vals.push(v);
				} 
			}
			var resultValue = 0.00;
			var fixed = 0;
			if(typeof(summary)=="function"){
				resultValue = summary(vals);
			}else if(summary.indexOf("average")==0){
				var parts = summary.split(":");
				fixed = (parts[1] != null) ? parts[1] : 0;
				var cnt = 0;
				$(vals).each(function(i,value){
					cnt++;
					resultValue += parseFloat(value);
				});
				resultValue = resultValue / cnt;
				resultValue = resultValue.toFixed(fixed);
			}else if(summary.indexOf("sum")==0){
				var parts = summary.split(":");
				fixed = (parts[1] != null) ? parts[1] : 0;
				$(vals).each(function(i,value){
					resultValue += parseFloat(value);
				});
				resultValue = resultValue.toFixed(fixed);
			}
			//push to the reslut array
			data.push(resultValue);
		}else{
			data.push("");
		}
	}
	if(data){
		var footer = $('<tr id="trSummary" style="background:gray"></tr>');
		$(data).each(function(i,v){
			var div = $('<div style="text-align: right;">' + v + '</div>'); 
			$(footer).append($('<td align="right"></td>').append(div)); 
		});
		$(tbody).append(footer); 
	}
}

然后在addData中加入,加在$(t).append(tbody)后面,也就是说,先同原来的逻辑一样渲染整个grid,再加入特定的summary行。
$(t).append(tbody);
				
//06012011 add by Bright Zheng
if(isSummary){
	this.appendSummaryFooter(tbody);
}

我这里支持合计sum,平均average,如果不能满足需求,也可以自定义。
格式如下:sum|average:scale。可以自定义精度。
使用举例:
{display: 'Price_a', name : 'price', width : 60, align: 'right',summary: 'average:2'},//sum
{display: 'Price_s', name : 'price', width : 60, align: 'right',summary: 'sum:0'},//average
{display: 'Price_c', name : 'price', width : 60, align: 'right',summary: customSum},//custom

自定义的小计函数如下:
//custom summary function
function customSum(arrayOfValue){
	var resultValue = 0.00;
	$(arrayOfValue).each(function(i,value){
		resultValue += parseFloat(value);
	});
	return resultValue.toFixed(3);
}



3.加载过程的提示消息处理。这一点原来有点搞,一旦你不启用翻页,连加载提示消息都没了,这个有点过分——未必所有的list都需要翻页的吧。
这里涉及几个小地方的调整,我就懒得贴代码了,这里提一下即可,有兴趣的自己看。


最后附上我在用的最新代码,对了,我废掉了xml格式的支持,JSON就挺好了,性能好还省带宽。


参考:
jQuery插件flexigrid使用总结: http://www.iteye.com/topic/611837
   发表时间:2011-03-23  
刚要研究flexigrid,学习一下。
0 请登录后投票
   发表时间:2011-04-04  
如何根据本行其它某个单元格的值作判断,格式化行背景?
0 请登录后投票
   发表时间:2011-04-05   最后修改:2011-04-05
arong 写道
如何根据本行其它某个单元格的值作判断,格式化行背景?

这一点我做过更新,这样可以保证传递给自定义render函数的参数为:
if (cm.render) {
    //07/02/2011 by bright: let custom render more powerful
    //val = cm.render(value);
    val = cm.render(val, row);
}



这个小小的改进却极大的方便了获取本行其他任何元素的可能,请参考。
0 请登录后投票
论坛首页 Web前端技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics