Dorado 9 : DataGrid(DCUG)

基本说明

DataGrid的基本属性和使用方法与Grid一致,本文不再做详细说明,详情请参考:Grid(DCUG)

与Grid最大的不同是:Grid通过items属性定义内部的数据行,而DataGrid通过其所设定的DataSet和DataPath属性所计算得到的EntityList作为自身的数据行。

另外由于DataSet的数据绑定效果,如果我们希望调整DataGrid中的数据,只要直接修改DataSet中的数据,DataSet会自动通知所绑定的DataGrid刷新内部数据行。

关于DataSet和DataPath的设定和使用技巧请参考:DataControl(DCUG)#显示一个集合类对象

详细属性说明

autoCreateColumns

是否自动根据绑定的Entity的DataType自动创建其中的表格列。

如下图虽然我们未在DataGrid中定义与DataType相关的DataColumn:

但是由于设置了autoCreateColumns属性为true,则在运行时,会自动根据关联的Entity对象的DataType创建DataColumn,效果如下:


filterMode

在Grid中我们详细介绍过Grid的过滤栏的功能。如果设置Grid的showFilterBar为true后就可以在Grid中看到过滤栏:

由于Grid中的数据都是定义在items属性中的,因此其过滤工作也是在客户端完成的,也就是说只能对客户端的数据做过滤。

DataGrid通常情况下可能会有分页处理,如果仅仅是客户端的过滤可能并不是客户所需要的功能,在更多的业务场景中用户可能更希望你是一个数据库的查询过滤。

因此DataGrid提供了filterMode属性:

它支持两种过滤查询:clientSide和serverSide。

其中clientSide与Grid的过滤栏查询一样,就是基于Grid中当前可见记录的查询。

如果设置为serverSide查询,那么过滤栏中的查询条件将会被传递到服务器的DataProvider中,由DataProvider处理查询参数Criteria。

参数criteria对应的Java类为:com.bstek.dorado.data.provider.Criteria.java。

@DataProvider
public void getAll(Page<Order> page, Criteria criteria) throws Exception {
	if (null!=criteria && criteria.getCriterions().size()>0){
		List<Criterion> criterions = criteria.getCriterions();
		for(Criterion criterion: criterions){
			if (criterion instanceof SingleValueFilterCriterion){
				SingleValueFilterCriterion filterCriterion = (SingleValueFilterCriterion)criterion;
				String property = filterCriterion.getProperty();//要查询的属性
				String value = (String)filterCriterion.getValue();//查询属性值
			}
		}
		//TODO,根据上面客户端传过来的查询条件,查询数据库并返回查询结果
	}
}

详细说明参考Java-API或Criteria对象使用说明

sortMode

在Grid或DataGrid中单击页眉中的列标题,Grid会自动根据当前列数据的大小正序或反序排列,如下图:

ID正序排列(注意id标题栏中的小三角图标):

ID反序排列(注意id标题栏中的小三角图标):

默认情况下这种排序机制是在客户端完成的,是基于客户端已有的数据所做的排序处理。

在分批数据下载机制下,如分页等,如果我们希望对所有的数据做排序处理,那么就需要设定DataGrid的sorgMode属性为serverSide:

采用serverSide的排序模式后,其排序机制的实现就要有Java后台完成了。在定义DataProvider代码的时候要声明com.bstek.dorado.data.provider.Criteria参数,相关排序的详细信息都包含在其中:

@DataProvider
public void getAll(Page<Order> page, Criteria criteria) throws Exception {
	if (null!=criteria && criteria.getOrders().size()>0){
		com.bstek.dorado.data.provider.Order propertyOrder = criteria.getOrders().get(0);
		String property = propertyOrder.getProperty();//要排序的属性
		boolean isDesc = propertyOrder.isDesc();//正序还是反序
		//TODO,根据上面客户端传过来的排序设置,查询数据库并返回查询结果
	}
}

上面的逻辑中你只要实现TODO中的代码就能实现一个serverSide的排序功能。

另外如果你是采用Hibernate中面向对象的开发模式,那还可以考虑使用Hibernate-addon中的工具类HibernateUtils实现:

@DataProvider
public void getAll(Page<Order> page, Criteria criteria) throws Exception {
	DetachedCriteria detachedCriteria = DetachedCriteria
			.forClass(Order.class);
	if (criteria != null) {
		orderDao.find(page,
				HibernateUtils.createFilter(detachedCriteria, criteria));
	} else {
		orderDao.find(page);
	}
}

HibernateUtils工具类会自动的将com.bstek.dorado.data.provider.Criteria参数转换为Hibernate中的org.hibernate.criterion.DetachedCriteria对象,简化代码的编写。

常用技巧

onRenderCell

通常我们都是利用DataColumn的onRenderCell实现单元格的渲染控制,如果我们想自定义单元格中的信息,如:显示超链接,显示图片,显示按钮,多个DataColumn中的值合并显示等等,这些都是通过onRenderCell事件实现的,并且其中最常用的一个js方法为xCreate,

下面我们列举其中的几种常见的场景(关于xCreate更详细的说明请参考client-api中的文档):

参考文档(自定义渲染的表格(sample-center))

表格超链接

效果如下:

实现办法

找到对应的DataColumn的onRenderCell事件:

其中编写代码:

jQuery(arg.dom).empty();
jQuery(arg.dom).xCreate({
	tagName: "A",
	href: "#",
	content: arg.data.get("shipCountry"),
	onclick: function() {
		alert(arg.data.get("shipCountry"));
	}
});

如果希望有多个链接,可以通过如下代码:

jQuery(arg.dom).empty();
jQuery(arg.dom).xCreate([{
		tagName: "A",
		href: "#",
		content: arg.data.get("shipCountry") + "1",
		onclick: function(){
			alert(arg.data.get("shipCountry"));
		}
	},{
		tagName: "A",
		href: "#",
		content: arg.data.get("shipCountry") + "2",
		style:"padding-left:20px",	
		onclick: function(){
			alert(arg.data.get("shipCountry"));
		}
	}
]);

数据格式化

显示效果如下的价格:

如果希望对数字或日期类型的数据做格式化的设置,可以通过DataColumn的displayFormat属性设置,如本例:

当然对于绑定DataSet的DataGrid中,我们更普遍的使用习惯还是将displayFormat配置到对应的DataType中,DataColumn中不设定displayFormat,让它自动的从DataType中获取。

自定义格式化

通过displayFormat可以做一些系统内置的格式化支持,可支持的格式化格式请参考jsdoc中的:

  • Date的formatDate()
如果我们有自己的格式化规则,那么可以通过DataColumn的onRendererCell方法定义:
// @Bind #gridPhones.#price.onRenderCell
!function(arg) {
	arg.dom.innerText = "平均:"
			+ dorado.util.Common.formatFloat(arg.data.get("price"), "#,##0");//调用自定义格式化规则,并将计算结果设置到dom的innerText中。
}

表格多列合并显示

如下图中的"体积(mm)"列

它实际上是对象的三个属性拼合而成的字符串:

JS实现代码如:

//@Bind #gridPhones.#size.onRenderCell
!function(arg) {
	var entity = arg.data;
	arg.dom.innerText = entity.get("size.length") + " x " + entity.get("size.width") + " x " + entity.get("size.height");
}

表格中显示按钮

效果如下图中的operation数据列:

实现办法(在Grid的onCreate中设定自定义的DataColumn的renderer对象):

var OperationCellRenderer = $extend(dorado.widget.grid.SubControlCellRenderer,
		{
			createSubControl : function(arg) {
				if (arg.data.rowType)
					return null;

				return new dorado.widget.Button({
					toggleable : true,
					onClick : function(self) {
						var entity = arg.data, originPrice, price;
						if (self.get("toggled")) {
							originPrice = entity.get("price");
							price = parseInt(originPrice * 0.8);
							entity.set({
								discount : true,
								price : price,
								originPrice : originPrice
							});
						} else {
							originPrice = entity.get("price");
							price = entity.get("originPrice");
							entity.set({
								discount : false,
								price : price
							});
						}
						dorado.widget.NotifyTipManager.notify(entity
								.get("product")
								+ "的价格已由" + originPrice + "调整为" + price + "!");
					}
				});
			},

			refreshSubControl : function(button, arg) {
				var storage = arg.data.get("storage");
				button.set({
					caption : (storage > 0) ? "折扣" : "无货",
					disabled : !(storage > 0),
					toggled : arg.data.get("discount")
				});
			}
		});

// @Bind #gridPhones.onCreate
!function(self) {
	self.set("&operation.renderer", new OperationCellRenderer());
}

表格中显示图片

效果如下图中的图片列:

可以通过实现该类的onRenderCell事件实现:

// @Bind #gridPhones.#image.onRenderCell
!function(arg) {
	$(arg.dom).empty().xCreate(
			{
				tagName : "IMG",
				src : $url(">dorado/res/com/bstek/dorado/sample/data/images/"
						+ arg.data.get("product") + "-24.png"),
				style : "margin: 2px"
			});
}

动态控制行只读或可编辑

DataGrid与DataSet绑定,当DataGrid定位行动作发生的时候会触发DataSet对应DataType的onCurrentChange事件,利用这种机制我们可以在onCurrentChange事件中动态控制Grid的可编辑特性:

// @Bind @Phone.onCurrentChange
!function(self, arg, gridPhones) {
	if (arg.newCurrent && arg.newCurrent.get("storage")>100){
		gridPhones.set("readOnly", true);
	}else{
		gridPhones.set("readOnly", false);
	}
}

上面的gridPhones为页面上的DataGrid。

另外由于Dorado这种数据模型编程机制,如果我们直接设置DataSet的readOnly属性,它会导致与之绑定的其他可视化控件的只读状态发生变化,如DataGrid, TextEditor, AutoForm等等,因此在更多的情况下我们会将上面的代码改写为:

// @Bind @Phone.onCurrentChange
!function(self, arg, dsPhones) {
	if (arg.newCurrent && arg.newCurrent.get("storage")>100){
		dsPhones.set("readOnly", true);
	}else{
		dsPhones.set("readOnly", false);
	}
}

根据行列动态决定某一个单元格只读或可编辑

与上面类似的,基于数据模型编码方式,如果想控制单个单元格的只读属性,可以通过如下方式设定:

// @Bind @Phone.onCurrentChange
!function(self, arg) {
	if (arg.newCurrent && arg.newCurrent.get("storage")>100){
		self.getPropertyDef("storage").set("readOnly", true);
	}else{
		self.getPropertyDef("storage").set("readOnly", false);
	}
}

如何在服务器端动态的为Grid添加DataColumn

可以利用动态视图技术实现,首先为DataGrid控件定义一个监听器,listener属性设置好。listener定义方法参考:监听器

参考代码:

	public void onGridInit(DataGrid gridProduct) {
		
		ColumnGroup columnGroup = new ColumnGroup();
		columnGroup.setCaption("分组列");
		DataColumn column1 = new DataColumn();
		column1.setName("productId");
		DataColumn column2 = new DataColumn();
		column2.setName("productName");
		columnGroup.addColumn(column2);
		gridProduct.addColumn(columnGroup);
		
	}

由于Dorado是基于数据模型管理数据的,因此你还要动态的创建DataType中对应的PropertyDef, 动态创建方法参考:动态私有DataType.

参考代码:

	public void onInit(ViewConfig viewConfig) throws Exception {
		PropertyDef propertyDef;

		EntityDataType dataTypeProduct = (EntityDataType) viewConfig
				.getDataType("Product");

		propertyDef = new BasePropertyDef("productId");
		propertyDef.setDataType(viewConfig.getDataType("long"));
		propertyDef.setLabel("编码");
		propertyDef.setReadOnly(true);
		dataTypeProduct.addPropertyDef(propertyDef);

		propertyDef = new BasePropertyDef("productName");
		propertyDef.setLabel("名称");
		dataTypeProduct.addPropertyDef(propertyDef);
	}