ExtJS里的Grid是UI控件的一个亮点,但是用起来比较麻烦,要编写太多的配置项,创建Grid的地方往往代码很长很长
在设计框架时,就考虑了以下几点:
1、Grid要封装的尽可能的简单,创建Grid代码要简单(最关键的是要降低脚本代码量,提高脚本的可读性与维护性)
2、Grid项变更时要尽量简单
3、Grid扩展列显示样式时也要尽量简单些
4、要能配合Command模式的操作(支持在配置文件里刷新)
5、要支持Command列
最后就做了这么两个东西出来:
QueryGrid
QueryAllGrid
两者的区别是QueryAllGrid不带分页功能
后来又在这两个基础上继续继承了其他的Grid,例如带自动连接查询配置项的Grid
封装后,在遇到查询Grid时,就简单了
例如,用户查询时,上面是个用户查询条件的FormPanel(叫usersearchpanel),下面是个放置GridPanel的userquerypanel,里面放了个usergrid
userquerypanel代码是这么写的:
var userquerypanel = new Ext.Panel({ width: '100%', height: 500, items: [usergrid] });
usergrid是这么写的:
var usergrid = new QueryGrid({ title: '查询结果', frame: true, moniker: 'UserInfo', gridName: 'user', queryPanel: usersearchpanel, width: '100%', height: 450 });
按钮条上有个查询按钮,代码就是"usergrid.read();"
当点击查询按钮时,usergrid就显示出查询结果了
也就是说,在前台,创建一个Grid,只需要给Grid三个参数:查询的数据类型,Grid的名字,查询参数的panel,就可以自动生成Grid的结果了
QueryGrid的构造代码是这样的:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 function QueryGrid(config) { 2 this.moniker = config.moniker; 3 this.gridName = config.gridName; 4 this.pageSize = 20; 5 if (config.pageSize != null) { 6 this.pageSize = config.pageSize; 7 } 8 if (config.sm == null) { 9 this.sm = new Ext.grid.CheckboxSelectionModel(); 10 } 11 this.url = '/Query/Read'; 12 if (config.url != null) { 13 this.url = config.url; 14 } 15 var cm = new Ext.grid.ColumnModel(eval(GridBuilder.getGridDef(config.gridName))); 16 var ds = new Ext.data.Store({ 17 url: this.url, 18 listeners: { 19 loadexception: function(s1, s2, s3, s4) { 20 alert('载入数据失败!' + Ext.util.JSON.decode(s3.responseText).msg); 21 } 22 }, 23 reader: new Ext.data.JsonReader({ totalProperty: 'results', root: 'rows' }, eval(GridBuilder.getGridReader(config.gridName))) 24 }); 25 config.cm = cm; 26 config.ds = ds; 27 28 config.bbar = new Ext.PagingToolbar({ 29 pageSize: this.pageSize, 30 store: ds, 31 displayInfo: true, 32 displayMsg: '第 {0} - {1} 条 共 {2} 条', 33 emptyMsg: "没有记录" 34 }); 35 QueryGrid.superclass.constructor.call(this, config); 36 }; 37 38 Ext.extend(QueryGrid, Ext.grid.GridPanel, { 39 read: function() { 40 if ((this.queryPanel != null) && (this.querypanel != 'undefined')) { 41 var s = this.queryPanel.getForm().getValues(true); 42 Ext.apply(this.store.baseParams, { query: s }); 43 } 44 Ext.apply(this.store.baseParams, { moniker: this.moniker, gridname: this.gridName }); 45 this.store.load(); 46 }, 47 doDelete: function(id) { 48 var grid = this; 49 Ext.MessageBox.confirm('提示', '确认要删除该行么?', function(msgid) { 50 if (msgid == 'yes') { 51 var requestconfig = { 52 waitmsg: '正在删除数据', 53 waitTitle: '提示', 54 url: '/Data/Delete?Moniker=' + grid.moniker + '&ID=' + id, 55 method: 'POST', 56 success: function(form, action) { 57 Ext.Msg.alert('提示', '删除成功'); 58 grid.store.load(); 59 }, 60 failure: function(form, action) { 61 Ext.Msg.alert('提示', '删除失败:' + action.result.msg); 62 } 63 } 64 Ext.Ajax.request(requestconfig); 65 } 66 }); 67 } 68 });
QueryAllGrid少了个bbar,这样就不分页了
从代码里可以看到的是,QueryGrid的实例构造时,按提供的名称配置从GridBuilder里获取了这个Grid的定义,并将定义转化成为这个Grid的cm
也就是说,Grid的标题栏其实是配置出来的
然后QueryGrid提供了read这个刷新方法和doDelete这个删除指定数据的方法,这样在command里,就可以配置查询为grid.read,read时会把提供的FormPanel里的数据序列化后做为参数传递给后台
删除一行数据的功能为grid.doDelete({'id'})
GridBuilder这个类负责在系统启动后(进入页面时)通过Ajax操作一次性加载所有的表格的定义,然后提供给所有的表格
当然,其实也考虑过所有的表格可以选择开始时加载一个空的定义,然后显示时再通过Ajax操作获取本表的定义后通过reconfigure方法来重新设置表格列,这样启动时少载入些信息,也不需要GridBuilder,代价是每个表格要额外的访问一次表格定义
表格的列是通过配置文件定义的,成如下模式:(以用户示例)
<Grid name="user" checkbox="true"> <Column name="gender" caption="" type="common" field="Gender" width="40" render="gendercolumnrender"/> <Column name="username" align="left" caption="名称" type="common" field="UserName" width="100"/> <Column name="phone" caption="电话" type="common" field="Telephone" width="100"/> <Column name="mobile" caption="手机" type="common" field="Mobile" width="150"/> <Column name="email" caption="电子邮件" type="common" field="Email" width="200"/> <Column name="state" caption="状态" type="global" field="State" key="userstate" width="80"/> <Column name="address" caption="地址" type="common" field="address" width="100"/> <Column name="command" width="150" type="command" caption="操作" field="command" command="usergrid" /> </Grid>
这样,如果要修正显示的列,无需修改代码,只需要修改配置文件(不过修改后要重启网站,因为配置文件是一次性加载的)
系统支持如下类型的列:
common,普通类型显示
image,字段的内容将被转换成对应的图形
global,把Int值转换成对应的字符串
foreign,外键连接字段,从外键KEY转换成对应的显示字段
link,显示成<a>样式,点击时执行里面设置的脚本
command,Command模式复用,根据指定的command创建操作列
在这些基础类型的列之外,如果我们遇到了特殊格式或要求的列(例如,要显示成美元$样式或人民币¥样式),则可以在列的属性上指定一个对应的render的IOC对象来处理
例如,编程时遇到个问题,就是日期控件保存到数据库里的数据,读出到Grid里时,显示的就是比较奇怪的字符串,这时可以加载一个render="datecolumnrender"并指定format格式为"yyyy-MM-dd",这个render的功能就是对每行的数据指定的字段,将其从源格式转换成日期字符串格式
遇到这些问题,就完全无需在前台对脚本进行改动来适应,也无需在逻辑层做任何处理,而只需要使用正确的render去处理
而在Controller层是如此处理的:
执行QueryController的Read方法时,首先获取参数,得到查询数据类型,然后根据配置获取该数据类型定义的SecurityHandler和DataHandler(参见核心设计模式之二),判断当前用户具备查询权限后,调用DataHandler的方法获取到结果,然后获取Grid的GridRender来对数据做渲染处理,这个GridRender前面没介绍,是负责对所有的列进行格式处理的,例如,把global列的Int值转换成对应的字符串,而如果一个列上定义了render,则GridRender会调用对应的ColumnRender来渲染这个列,最后,将渲染的结果生成Json数据传递给grid的store
总结:ExtFrame里的Grid封装有以下优点:
1、代码创建简单
2、封装的刷新、删除等功能易用
3、Grid修改简单
4、修改显示内容时无需修改代码
5、提供扩展显示容易