How to use the Dojo EnhanceDGrid control to save data asynchronous

This article introduces how to use Dojo EnhancedGrid control for asynchronous data transmission and storage, allowing users Operating the table in the browser can be as convenient as Excel, and the data can be automatically saved in the database. Dojo EnhancedGrid provides a complete set of APIs and solutions, avoiding software developers to develop tedious page code, and easily implement page data manipulation functions.

Design Summary: Introduction Application and architecture of the sample application

Business scenario:

The client needs to make a company information management interface, and the administrator can find out what he needs by clicking the query button after entering Information, the page does not need to be refreshed during the query process, reducing the user’s waiting time.

Front frame design: struts 2.0+Dojo 1.7.1

Business logic: Spring 3.1

Persistence layer: mybatis 3.0.6 + DB2 9.7

Presentation layer : Introduce the design of the presentation layer of the sample application

Introduction to Dojo EnhancedGrid

As the name implies, EnhancedGrid is an enhanced version of Grid, which has been available since Dojo 1.4. Its existence is due to the fact that although the original DataGrid has powerful functions, it is rather deadly written in many places and is not easy to expand. Therefore, the EnhancedGrid inherited from DataGrid provides a more flexible plug-in mechanism. On the one hand, it can make subsequent developers less restricted by the existing code; on the other hand, it can also reduce the weight of the increasingly versatile Grid (unused plug-ins). Can not be loaded). EnhancedGrid provides the following new plug-in functions:

  • Nested Sorting-Multi-line sorting.
  • Indirect Selection-Select rows by radio buttons and check buttons.
  • Filter – Support custom type rules to filter the content of the table.
  • Exporter – Export table content to various content.
  • DnD – Drag-and-drop (drag and drop) supports row/column/table cells, both inside and outside the cell.
  • Pagination -Paging function.
  • CellMerge – Combine adjacent table cells into one row.
  • Search – Find the content in the table by means of regular expressions and string matching.

EnhancedGrid is very similar to the client/server architecture, basically a grid is an Excel spreadsheet, usually it is wrapped In a large table, borrowing from the HTML specification, it uses its own form to display the table. The specific display form is as follows:

Figure 1. EnhancedGrid example image

Figure 1. EnhancedGrid example image

Property introduction:

Dojo EnhancedGrid has many properties, here are only a few commonly used properties:

Table 1. Introduction to the common attributes of EnhancedGrid
Properties Explanation
field the name of the column in the data set
width Column width
editable Can edit: true-yes, false-no
formatter A function that can change the table cell value

How Use Dojo EnhancedGrid

Introduce Dojo EnhancedGrid control

To use Dojo EnhancedGrid control, first add this control to the page:

Listing 1. Introducing Dojo code in the JS file
 require(['dojo/_base/array', 'dojo/_base/lang','dojo/_base/event','dojo/on','dojox/grid/EnhancedGrid','dojo/data/ItemFileWriteStore','dijit/form/Button','dojo/dom ','dojo/parser','dojo/domReady!'], function(array, lang, event, on, EnhancedGrid, ItemFileWriteStore, Button, dom, parser){ parser.parse();}

declaration The header of the form

Listing 2. Use JSON to define the header in Dojo EnhancedGrid
 // Define the layout, that is, define the column heading of the title, width var layout = [[ {'name':'Id','field':'id', ' width': '3%', editable: true}, {'name':'Agent Te am','field':'agentTeam','width': '8%', editable: true}, {'name':'Community UUid','field':'communityUuid','width': '17% ', \ editable: true}, {'name':'View members','field':'communityUuid','width': '10%',\ formatter: formatHref}, {'name':'Forum name' ,'field':'forum','width': '10%', editable: true}, {'name':'Forum Type','field':'forumType','width': '6%', editable: true,\ type: dojox.grid.cells.Select, options:["Awards","Program","FAQ","HelpDesk",\ "Best Practices"], values: ['1', '2 ','3','4','5' ],formatter: formatForumType}, {'name':'URL','field':'url','width':'auto', editable: true}] ];

Create an EnhancedGrid control object

Listing 3. Create an EnhancedGrid control object
 grid = new EnhancedGrid({ id:'grid', store: store,// Connect datastore control structure: layout, rowSelector: '20px'}); /* Add grid control To the HTML div tag */ grid.placeAt("gridDiv"); );

The following is to customize a new function to save data asynchronously to the database:

Listing 4. Custom function to add data asynchronously
 function onNew(item) {// Get the content of the form unit var agentTeam=item.agentTeam; var communityUuid=item.communityUuid ; var forum=item.forum; var url=item.url; var forumType=item.forumType; dojo.xhrPost({ //The URL of the request url: "/yourproject/new", // The format of the content to be processed handleAs: "json", // Request parameter content: {agentTeam: agentTeam, communityUuid: communityUuid, forum: forum, url: url, forumType: forumType }, // error handling function error: function(error, ioArgs) {alert(error.message);} }) ; // Automatically refresh the page after adding a record; setTimeout(function() {window.location.reload();},2000); }

dojo.xhrPost uses RESTFUL POST to add new data.

add new and delete buttons:

Listing 5. New button HTML code
   Add One Row        Remove Selected Rows  

< span style="margin:0px; padding:0px; border:0px; outline:0px; font-size:undefined; vertical-align:baseline">bind the function to the EnhancedGrid event

< p style="margin-top:0px; margin-bottom:0px; padding-top:6px; padding-bottom:6px; border:0px; outline:0px; vertical-align:baseline; color:rgb(34,34, 34); line-height:1.5em; font-size:1.166em!important"> Finally bind the custom function to the EnhancedGrid event:

Listing 6. Binding event function code
 grid = new EnhancedGrid({ id:'grid', store: store,/ / Connect the datastore control structure: layout, rowSelector: '20px'});/ /* Add the grid control to the HTML div tag */ grid.placeAt("gridDiv"); // The following is a binding function to a specific event // Bind the function to the new function dojo.connect(store, "onNew", this,onNew); // Bind the function to the modified function dojo.connect(store, "onSet", this,onSet); // Bind Set function to delete function dojo.connect(store, "onDel ete", this,onDelete); /* Add a click processing event for button2 button*/ on(button2,'click', function(e){ /* set the properties for the new item: */ var myNewItem = {id: '', agentTeam:'new',communityUuid:'new',forum:'new', \ url:'new', forumType: 0}; /* Insert the new item into the store:*/ store.newItem(myNewItem );// New row} ); /* Add a click processing event for the button1 button*/ on(button1,'click', function(e){ /* Select the selected row*/ var items = grid.selection. getSelected(); if(items.length){ if(!confirm("Are you sure to delete?")){ return false;} /* loop through the selected rows*/ array.forEach(items, function(selectedItem ){ if(selectedItem !== null){ /* Delete the item from the data store: */ store.deleteItem(selectedItem);} /* end if */ }); /* end forEach */ }else{ alert ("Please select row(s)");} event.stop(e);} );

dojo.connect model

Here is used dojo.connect model:dojo. connect(store, “onNew”, this,onNew);

As long as the user initiates the onNew event (Create a new record), the onNew function will be triggered (users can define by themselves).

In every popular toolkit, there are always some unusually brilliant shining points. dojo.connect is a heavy function related to the JavaScript event mechanism in the Dojo toolkit.

In the use of JavaScript, we often need to listen to the triggering of certain events, and then perform the corresponding (function) handle. For example, most commonly, when the login node of the login page is clicked, JavaScript can detect it, and then send the user login information to the background. On the surface, dojo.connect is a simple function that completes a simple event correlation function. However, due to the flexible configuration of the support parameters and the organic combination with other Dojo functions, it can sometimes cause some wonderful effects. And exploring these effects and using them to solve some specific problems is exactly the charm of technology!

Here are some dojo.connect features that can be thought of in a very simple way. Interested comrades are also welcome to give more application scenarios.

Service layer: introduce sample applications The design of the service layer

The service layer of this article uses S pring Framework, I won’t say more about other advantages of Spring here. Those who are interested can check it out in the Spring community. Directly upload the code:

清单7.Spring 配置文件代码
                                                         

上面在 datasource 使用了 JNDI,学者可以将它替换为自己的 JDBC 连接方式。

数据访问层:介绍示例应用程序的数据访问层的设计

本例中使用 My Batis 来实现数据访问层的设计。 MyBatis 作为持久层框架,其主要思想是将程序中的大量 SQL 语句剥离出来,配置在配置文件中,实现 SQL 的灵活配置。这样做的好处是将 SQL 与程序代码分离,可以在不修改程序代码的情况下,直接在配置文件中修改 SQL。下面给出本例子中的部分代码并解释其意思。

Dao 接口类 AdminMaintainDao 代码

清单 8. AdminMaintainDao 代码
 public interface AdminMaintainDao { 	 /** 从数据库中查询所有的记录 *  */ 	 public List getAllAdminMaintain(); 	 /** 新增记录 *  */ 	 public void add(AdminMaintain am);  /** 修改记录 *  */ 	 public void update(AdminMaintain am);  /** 删除记录 *  */ 	 public void delete(String id); 	 }

清单 9. 数据映射配置文件 AdminMaintainDaoMapper.xml 的代码
      	               	      	      	      	         	  	    	       	                                    insert into TLD.Admin_Maintain(agentTeam, forum, url,forum_Type,COMMUNITY_UUID)	   values (#{agentTeam}, #{forum}, #{url},#{forumType},#{co mmunityUuid}) 	  	     	   update TLD.Admin_Maintain set agentTeam=#{agentTeam}, forum=#{forum}, 	   url=#{url},forum_Type=#{forumType},COMMUNITY_UUID=#{communityUuid} 	   where maintain_id=#{id} 	  	              delete from TLD.Admin_Maintain where maintain_id = #{id}            

清单 10. 数据库表定义语句
 CREATE TABLE "TLD"."ADMIN_MAINTAIN"  (  "MAINTAIN_ID" BIGINT NOT NULL GENERATED ALWAYS AS IDENTITY (      START WITH +0      INCREMENT BY +1      MINVALUE +0      MAXVALUE +9223372036854775807      NO CYCLE      CACHE 20      NO ORDER ) ,   "AGENTTEAM" CHAR(50) NOT NULL ,   "FORUM" CHAR(50) NOT NULL ,   "URL" CHAR(200) NOT NULL ,   "FORUM_TYPE" INTEGER NOT NULL ,   "COMMUNITY_UUID" CHAR(50) NOT NULL )   		 IN "USERSPACE1" ;

下面对上面的 dao 代码进行单元测试:

清单 11.DAO 单元测试代码
 public class TestAdminMaintainDao { 	 private Logger logger = Logger.getLogger(this.getClass()); 	 private ClassPathXmlApplicationContext ctx = \	 new ClassPathXmlApplicationContext("applicationContext.xml"); 	 // 从配置文件中初始化实例     private AdminMaintainDao dao = \     (AdminMaintainDao)ctx.getBean("adminMaintainDao");   /** 测试查询 */  @Test  public void testGetAllAdminMaintain() { 					  List list=dao.getAllAdminMaintain(); 	  for (AdminMaintain am : list) { 			            logger.info(am.getAgentTeam()+" : "+am.getUrl()); 	      //add your code here; 	  } 		  }  /** 测试新增 */  @Test  public void testAdd() { 				  AdminMaintain am=new AdminMaintain(); 	  am.setAgentTeam("C"); 	  am.setCommunityUuid("UUidssss8080-980sf"); 	  am.setForum("C Awards"); 	  am.setUrl("http:// c wards"); 	  am.setForumType(1); 	  dao.add(am); 	    } 	 /** 测试修改 */  @Test  public void testUpdate() { 			  AdminMaintain am=new AdminMaintain(); 	  am.setId(11); 	  am.setAgentTeam("D"); 	  am.setCommunityUuid("sfs080-sfs");       am.setForum("D FAQ"); 	  am.setUrl("http:// D FAQ"); 	  am.setForumType(3); 	  dao.update(am); 	    } 	 /** 测试删除 */   @Test   public void testDelete() { 		  dao.delete("11");   }  }

测试:测试示例应用程序

登录到编辑页面,EnhancedG rid 就会从后台把记录列出来,点击“新增”按钮,就会新增一条空的记录。编辑该记录,编辑完后数据就自动的保存到后台数据库,选中所要删除的记录 ( 可以多选 ),点击删除按钮,确认删除后会把所选的记录全部删除,整个过程不用刷新页面就能完成,感觉就像在操作 Excel 表格一样 ( 如图 1),用户交互性很强。

结束语

EnhancedGrid 是一个功能强大、丰富的表格控件,提供伸缩式的表格数据渲染,还包括分页、排序、过滤、列移动、选择、编辑、键盘导航,导出和打印功能等等。 EnhancedGrid 与 store 的紧密联系使得在 Grid 中对数据处理进行配置非常容易,通过与 dojo.connect 在一起使用可以很容易的来绑定自定义函数来实现对表格数据的异步存储。

本文介绍如何利用 Dojo EnhancedGrid 控件进行数据异步传输和保存,让使用者在浏览器中操作表格能像 Excel 一样方便,并且能实现数据自动保存到数据库中。 Dojo EnhancedGrid 提供了一套完整的 API 和解决方案,避免了软件开发人员去开发繁琐的页面代码,轻松实现页面数据的操作功能。

本文介绍如何利用 Dojo EnhancedGrid 控件进行数据异步传输和保存,让使用者在浏览器中操作表格能像 Excel 一样方便,并且能实现数据自动保存到数据库中。 Dojo EnhancedGrid 提供了一套完整的 API 和解决方案,避免了软件开发人员去开发繁琐的页面代码,轻松实现页面数据的操作功能。

设计概要:介绍示例应用程序的应用和体系结构

业务场景:

客户需要做一个公司信息管理界面,管理员进入后只要点击查询按钮就能查出所需要的信息,在查询的过程中页面不需要刷新,减少用户的等待时间。

前台框架设计:struts 2.0+Dojo 1.7.1

业务逻辑:Spring 3.1

持久层:mybatis 3.0.6 + DB2 9.7

展现层:介绍示例应用程序的展现层的设计

Dojo EnhancedGrid 简介

顾名思义,EnhancedGrid 就是 Grid 的加强版,从 Dojo 1.4 开始就有了。它的存在是由于原先的 DataGrid 虽然功能强大,但很多地方写得比较死,不太便于扩展。因此,继承自 DataGrid 的 EnhancedGrid 就提供了一种较为灵活的插件机制,一方面能使后来的开发者较少受到现有代码的制约;另一方面也是为功能日益繁多的 Grid 瘦身(不用的插件可以不加载)。 EnhancedGrid 提供了如下一些新的插件功能:

  • Nested Sorting – 多行排序。
  • Indirect Selection – 通过单选按钮和复选按钮选择行。
  • Filter – 支持自定义类型规则来过滤表格的内容。
  • Exporter – 把表格内容导出到各种内容。
  • DnD – Drag-and-drop( 拖放 ) 支持行 / 列 / 表格单元,在单元内外都可以。
  • Pagination – 分页功能。
  • CellMerge – 合并相邻的表格单元到一行中。
  • Search – 通过正则表达式和字符串匹配的方式来查找表格中的内容。

EnhancedGrid 很像 client/server 架构,基本上一个 grid 就是一个 Excel spreadsheet, 通常它被包裹在一个大的表格里面,借于 HTML 的规范,它用自己的形式来展示表格。具体展现形式如下:

图 1. EnhancedGrid 例图

图 1. EnhancedGrid 例图

属性简介:

Dojo EnhancedGrid 有很多属性,这里仅介绍常用的几个属性:

表 1. EnhancedGrid 常用属性介绍
属性 解释
field 数据集里面列的名字
width 列的宽度
editable 能否编辑 :true – 能,false – 不能
formatter 可以改变表格单元值的函数

如何使用 Dojo EnhancedGrid

引入 Dojo EnhancedGrid 控件

要使用 Dojo EnhancedGrid 控件,首先要把此控件加入页面 :

清单 1. 在 JS 文件中引入 Dojo 代码
 require(['dojo/_base/array', 'dojo/_base/lang', 'dojo/_base/event',  'dojo/on', 'dojox/grid/EnhancedGrid', 'dojo/data/ItemFileWriteStore',  'dijit/form/Button', 'dojo/dom', 'dojo/parser', 'dojo/domReady!'],   function(array, lang, event, on, EnhancedGrid, ItemFileWriteStore,           Button, dom, parser){  parser.parse();}

声明表格的标头

清单 2. 在 Dojo EnhancedGrid 中使用 JSON 定义表头
 // 定义 layout 布局,就是定义标题的列标题,宽度 var layout = [[       {'name': 'Id', 'field': 'id', 'width': '3%', editable: true},       {'name': 'Agent Team', 'field': 'agentTeam', 'width': '8%', editable: true},       {'name': 'Community UUid', 'field': 'communityUuid', 'width': '17%', \       editable: true},       {'name': 'View members', 'field': 'communityUuid', 'width': '10%',\       formatter: formatHref},      {'name': 'Forum name', 'field': 'forum', 'width': '10%', editable: true},       {'name': 'Forum Type', 'field': 'forumType', 'width': '6%', editable: true,\       type: dojox.grid.cells.Select, options:["Awards","Program","FAQ","HelpDesk",\       "Best Practices"], values: [ '1', '2','3','4','5' ],formatter: formatForumType},  	  {'name': 'URL', 'field': 'url', 'width': 'auto', editable: true}  ]];

创建 EnhancedGrid 控件对象

< h5 id="listing3" style="margin:5px 0px 0px; padding:0px; border:0px; outline:0px; font-size:1.166em!important; vertical-align:baseline"> 清单 3. 创建 EnhancedGrid 控件对象

 grid = new EnhancedGrid({         id: 'grid',         store: store,// 连接 datastore 控件        structure: layout,         rowSelector: '20px'});    /* 把 grid 控件加入到 HTML div 标签中 */    grid.placeAt("gridDiv");  );

下面来自定义一个新增函数来实现异步保存数据到数据库中:

清单 4. 自定义函数来实现异步添加数据
 function onNew(item) {    // 得到表格单元的内容     var agentTeam=item.agentTeam; 	 var communityUuid=item.communityUuid;      var forum=item.forum; 	 var url=item.url; 	 var forumType=item.forumType; 		 dojo.xhrPost({        //The URL of the request        url: "/yourproject/new",        // 要处理内容的格式       handleAs: "json",        // 请求参数      content: { 				        agentTeam: agentTeam,         communityUuid: communityUuid, 	        forum: forum,         url: url,         forumType: forumType        },           // 错误处理函数      error: function(error, ioArgs) {          alert(error.message); 		    } 	   }); 	 // 添加记录后自动刷新页面 ;  setTimeout(function() {window.location.reload();},2000);  }

dojo.xhrPost 是使用 RESTFUL POST 的方式来添加新数据。

添加新增和删除按钮:

清单 5. 新增按钮 HTML 代码
           Add One Row                   Remove Selected Rows     

将函数绑定到 EnhancedGrid 事件上

最后将自定义函数绑定到 EnhancedGrid 事件上:

清单 6. 绑定事件函数代码
 grid = new EnhancedGrid({         id: 'grid',         store: store,// 连接 datastore 控件        structure: layout,         rowSelector: '20px'});/    /* 把 grid 控件加入到 HTML div 标签中 */    grid.placeAt("gridDiv");  // 以下为绑定函数到特定的事件 // 绑定函数到新增功能	 dojo.connect(store, "onNew", this,onNew);  // 绑定函数到修改功能	 dojo.connect(store, "onSet", this,onSet);  // 绑定函数到删除功能	 dojo.connect(store, "onDelete", this ,onDelete);     /* 为 button2 按钮添加 click 处理事件 */     on(button2,'click',        function(e){         /* set the properties for the new item: */         var myNewItem = {id: '', agentTeam: 'new',communityUuid:'new',forum: 'new', \        url: 'new', forumType: 0};         /* Insert the new item into the store:*/         store.newItem(myNewItem);// 新增行    }     );  /* 为 button1 按钮添加 click 处理事件 */    on(button1,'click',     function(e){  /* 选择被选中的行 */         var items = grid.selection.getSelected(); 		        if(items.length){            if(!confirm("Are you sure to delete?")){               return false; 					 }             /* 循环遍历所选择的行 */             array.forEach(items, function(selectedItem){                 if(selectedItem !== null){                     /* Delete the item from the data store: */                     store.deleteItem(selectedItem);                 } /* end if */             }); /* end forEach */         }else{            alert("Please select row(s)");  }         event. stop(e);     }  );

dojo.connect 模型

此处用到了 dojo.connect 模型:dojo.connect(store, “onNew”, this,onNew);

只要用户出发了 onNew 事件 ( 创建一条新记录 ),就会触发 onNew 函数(用户可以自己定义)。

每个流行的工具包中,总有一些异常出彩的闪光点。 dojo.connect 就是 Dojo 工具包中,与 JavaScript 事件机制相关的重磅功能。

在 JavaScript 的使用场景中,我们经常需要侦听某些事件的触发,然后进行相应的(函数)处理。比如最常见的,当点击登录页面的登录节点时,JavaScript 能够察觉到,并随之将用户登录信息发送到后台。从表面上看,dojo.connect 就是一个单纯的函数,完成单纯的事件关联功能。但由于支持参数的灵活配置及和其他 Dojo 函数的有机组合,有时候可以造成一些奇妙效果。而探索这些效果并用于解决一些特定的问题,正是技术的魅力所在吧!

这儿很浅显地讨论一些目前能想到的 dojo.connect 特性。也欢迎有兴趣的同志给出更多的应用场景。

服务层:介绍示例应用程序的服务层的设计

本文的服务层采用的是 Spring Framework, 关于 Spring 的其他优点这里不再多说,有兴趣的朋友可以到 Spring 社区去查看。直接上代码:

清单 7.Spring 配置文件代码
                                                         

上面在 datasource 使用了 JNDI,学者可以将它替换为自己的 JDBC 连接方式。

数据访问层:介绍示例应用程序的数据访问层的设计

本例中使用 MyBatis 来实现数据访问层的设计。 MyBatis 作为持久层框架,其主要思想是将程序中的大量 SQL 语句剥离出来,配置在配置文件中,实现 SQL 的灵活配置。这样做的好处是将 SQL 与程序代码分离,可以在不修改程序代码的情况下,直接在配置文件中修改 SQL。下面给出本例子中的部分代码并解释其意思。

Dao 接口类 AdminMaintainDao 代码

清单 8. AdminMaintainDao 代码
 public interface AdminMaintainDao { 	 /** 从数据库中查询所有的记录 *  */ 	 public List getAllAdminMaintain(); 	 /** 新增记录 *  */ 	 public void add(AdminMaintain am);  /** 修改记录 *  */ 	 public void update(AdminMaintain am);  /** 删除记录 *  */ 	 public void delete(String id); 	 }

清单 9. 数据映射配置文件 AdminMaintainDaoMapper.xml 的代码
      	               	      	      	      	         	  	    	       	                                    insert into TLD.Admin_Maintain(agentTeam, forum, url,forum_Type,COMMUNITY_UUID)	   values (#{agentTeam}, #{forum}, #{url},#{forumType},#{communityU uid}) 	  	     	   update TLD.Admin_Maintain set agentTeam=#{agentTeam}, forum=#{forum}, 	   url=#{url},forum_Type=#{forumType},COMMUNITY_UUID=#{communityUuid} 	   where maintain_id=#{id} 	  	              delete from TLD.Admin_Maintain where maintain_id = #{id}            

清单 10. 数据库表定义语句
 CREATE TABLE "TLD"."ADMIN_MAINTAIN"  (  "MAINTAIN_ID" BIGINT NOT NULL GENERATED ALWAYS AS IDENTITY (      START WITH +0      INCREMENT BY +1      MINVALUE +0      MAXVALUE +9223372036854775807      NO CYCLE      CACHE 20      NO ORDER ) ,   "AGENTTEAM" CHAR(50) NOT NULL ,   "FORUM" CHAR(50) NOT NULL ,   "URL" CHAR(200) NOT NULL ,   "FORUM_TYPE" INTEGER NOT NULL ,   "COMMUNITY_UUID" CHAR(50) NOT NULL )   		 IN "USERSPACE1" ;

下面对上面的 dao 代码进行单元测试:

清单 11.DAO 单元测试代码
 public class TestAdminMaintainDao { 	 private Logger logger = Logger.getLogger(this.getClass()); 	 private ClassPathXmlApplicationContext ctx = \	 new ClassPathXmlApplicationContext("applicationContext.xml"); 	 // 从配置文件中初始化实例     privat e AdminMaintainDao dao = \     (AdminMaintainDao)ctx.getBean("adminMaintainDao");   /** 测试查询 */  @Test  public void testGetAllAdminMaintain() { 					  List list=dao.getAllAdminMaintain(); 	  for (AdminMaintain am : list) { 			            logger.info(am.getAgentTeam()+" : "+am.getUrl()); 	      //add your code here; 	  } 		  }  /** 测试新增 */  @Test  public void testAdd() { 				  AdminMaintain am=new AdminMaintain(); 	  am.setAgentTeam("C"); 	  am.setCommunityUuid("UUidssss8080-980sf"); 	  am.setForum("C Awards"); 	  am.setUrl("http:// c wards"); 	  am.setForumType(1); 	  dao.add(am); 	    } 	 /** 测试修改 */  @Test  public void testUpdate() { 			  AdminMaintain am=new AdminMaintain(); 	  am.setId(11); 	  am.setAgentTeam("D"); 	  am.setCommunityUuid("sfs080-sfs");       am.setForum("D FAQ"); 	  am.setUrl("http:// D FAQ"); 	  am.setForumType(3); 	  dao.update(am); 	    } 	 /** 测试删除 */   @Test   public void testDelete() { 		  dao.delete("11");   }  }

测试:测试示例应用程序

登录到编辑页面,EnhancedGrid 就会从后台把记录列出来,点击“新增”按钮,就会新增一条空的记录。编辑该记录,编辑完后数据就自动的保存到后台数据库,选中所要删除的记录 ( 可以多选 ),点击删除按钮,确认删除后会把所选的记录全部删除,整个过程不用刷新页面就能完成,感觉就像在操作 Excel 表格一样 ( 如图 1),用户交互性很强。

结束语

EnhancedGrid 是一个功能强大、丰富的表格控件,提供伸缩式的表格数据渲染,还包括分页、排序、过滤、列移动、选择、编辑、键盘导航,导出和打印功能等等。 EnhancedGrid 与 store 的紧密联系使得在 Grid 中对数据处理进行配置非常容易,通过与 dojo.connect 在一起使用可以很容易的来绑定自定义函数来实现对表格数据的异步存储。

设计概要:介绍示例应用程序的应用和体系结构

业务场景:

客户需要做一个公司信息管理界面,管理员进入后只要点击查询按钮就能查出所需要的信息,在查询的过程中页面不需要刷新,减少用户的等待时间。

前台框架设计:struts 2.0+Dojo 1.7.1

业务逻辑:Spring 3.1

持久层:mybatis 3.0.6 + DB2 9.7

展现层:介绍示例应用程序的展现层的设计

Dojo EnhancedGrid 简介

顾名思义,EnhancedGrid 就是 Grid 的加强版,从 Dojo 1.4 开始就有了。它的存在是由于原先的 DataGrid 虽然功能强大,但很多地方写得比较死,不太便于扩展。因此,继承自 DataGrid 的 EnhancedGrid 就提供了一种较为灵活的插件机制,一方面能使后来的开发者较少受到现有代码的制约;另一方面也是为功能日益繁多的 Grid 瘦身(不用的插件可以不加载)。 EnhancedGrid 提供了如下一些新的插件功能:

  • Nested Sorting – 多行排序。
  • Indirect Selection – 通过单选按钮和复选按钮选择行。
  • Filter – 支持自定义类型规则来过滤表格的内容。
  • Exporter – 把表格内容导出到各种内容。
  • DnD – Drag-and-drop( 拖放 ) 支持行 / 列 / 表格单元,在单元内外都可以。
  • Pagination – 分页功能。
  • CellMerge – 合并相邻的表格单元到一行中。
  • Search – 通过正则表达式和字符串匹配的方式来查找表格中的内容。

EnhancedGrid 很像 client/server 架构,基本上一个 grid 就是一个 Excel spreadsheet, 通常它被包裹在一个大的表格里面,借于 HTML 的规范,它用自己的形式来展示表格。具体展现形式如下:

图 1. EnhancedGrid 例图

图 1. EnhancedGrid 例图

属性简介:

Dojo EnhancedGrid 有很多属性,这里仅介绍常用的几个属性:

表 1. EnhancedGrid 常用属性介绍
属性 解释
field 数据集里面列的名字
width 列的宽度
editable 能否编辑 :true – 能,false – 不能
formatter 可以改变表格单元值的函数

如何使用 Dojo EnhancedGrid

引入 Dojo EnhancedGrid 控件

要使用 Dojo EnhancedGrid 控件,首先要把此控件加入页面 :

清单 1. 在 JS 文件中引入 Dojo 代码
 require(['dojo/_base/array', 'dojo/_base/lang', 'dojo/_base/event',  'dojo/on', 'dojox/grid/EnhancedGrid', 'dojo/data/ItemFileWriteStore',  'dijit/form/Button', 'dojo/dom', 'dojo/parser', 'dojo/domReady!'],   function(array, lang, event, on, EnhancedGrid, ItemFileWriteStore,           Button, dom, parser){  parser.parse();}

声明表格的标头

清单 2. 在 Dojo EnhancedGrid 中使用 JSON 定义表头
 // 定义 layout 布局,就是定义标题的列标题,宽度 var layout = [[       {'name': 'Id', 'field': 'id', 'width': '3%', editable: true},       {'name': 'Agent Team', 'field': 'agentTeam', 'width': '8%', editable: true},       {'name': 'Community UUid', 'field': 'communityUuid', 'width': '17%', \       editable: true},       {'name': 'View members', 'field': 'communityUuid', 'width': '10%',\       formatter: formatHref},      {'name': 'Forum name', 'field': 'forum', 'width': '10%', editable: true},       {'name': 'Forum Type', 'field': 'forumType', 'width': '6%', editable: true,\       type: dojox.grid.cells.Select, options:["Awards","Program","FAQ","HelpDesk",\       "Best Practices"], values: [ '1', '2','3','4','5' ],formatter: formatForumType},  	  {'name': 'URL', 'field': 'url', 'width': 'auto', editable: true}  ]];

创建 EnhancedGrid 控件对象

清单 3. 创建 EnhancedGrid 控件对象
 grid = new EnhancedGrid({         id: 'grid',         store: store,// 连接 datastore 控件        structure: layout,         rowSelector: '20px'});    /* 把 grid 控件加入到 HTML div 标签中 */    grid.placeAt("gridDiv");  );

下面来自定义一个新增函数来实现异步保存数据到数据库中:

清单 4. 自定义函数来实现异步添加数据
 function onNew(item) {    // 得到表格单元的内容     var agentTeam=item.agentTeam; 	 var communityUuid=item.communityUuid;      var forum=item.forum; 	 var url=item.url; 	 var forumType=item.forumType; 		 dojo.xhrPost({        //The URL of the request        url: "/yourproject/new",        // 要处理内容的格式       handleAs: "json",        // 请求参数      content: { 				        agentTeam: agentTeam,         communityUuid: communityUuid, 	        forum: forum,         url: url,         forumType: forumType        },           // 错误处理函数      error: function(error, ioArgs) {          alert(error.message); 		    } 	   }); 	 // 添加记录后自动刷新页面 ;  setTimeout(function() {window.location.reload();},2000);  }

dojo.xhrPost 是使用 RESTFUL POST 的方式来添加新数据。

添加新增和删除按钮:

清单 5. 新增按钮 HTML 代码
           Add One Row                   Remove Selected Rows     

将函数绑定到 EnhancedGrid 事件上

最后将自定义函数绑定到 EnhancedGrid 事件上:

清单 6. 绑定事件函数代码
 grid = new EnhancedGrid({         id: 'grid',         store: store,// 连接 datastore 控件        structure: layout,         rowSelector: '20px'});/    /* 把 grid 控件加入到 HTML div 标签中 */    grid.placeAt("gridDiv");  // 以下为绑定函数到特定的事件 // 绑定函数到新增功能	 dojo.connect(store, "onNew", this,onNew);  // 绑定函数到修改功能	 dojo.connect(store, "onSet", this,onSet);  // 绑定函数到删除功能	 dojo.connect(store, "onDelete", this,onDelete) ;     /* 为 button2 按钮添加 click 处理事件 */     on(button2,'click',        function(e){         /* set the properties for the new item: */         var myNewItem = {id: '', agentTeam: 'new',communityUuid:'new',forum: 'new', \        url: 'new', forumType: 0};         /* Insert the new item into the store:*/         store.newItem(myNewItem);// 新增行    }     );  /* 为 button1 按钮添加 click 处理事件 */    on(button1,'click',     function(e){  /* 选择被选中的行 */         var items = grid.selection.getSelected(); 		        if(items.length){            if(!confirm("Are you sure to delete?")){               return false; 					 }             /* 循环遍历所选择的行 */             array.forEach(items, function(selectedItem){                 if(selectedItem !== null){                     /* Delete the item from the data store: */                     store.deleteItem(selectedItem);                 } /* end if */             }); /* end forEach */         }else{            alert("Please select row(s)");  }         event.stop(e);     }  );

dojo.connect 模型

此处用到了 dojo.connect 模型:dojo.connect(store, “onNew”, this,onNew);

只要用户出发了 onNew 事件 ( 创建一条新记录 ),就会触发 onNew 函数(用户可以自己定义)。

每个流行的工具包中,总有一些异常出彩的闪光点。 dojo.connect 就是 Dojo 工具包中,与 JavaScript 事件机制相关的重磅功能。

在 JavaScript 的使用场景中,我们经常需要侦听某些事件的触发,然后进行相应的(函数)处理。比如最常见的,当点击登录页面的登录节点时,JavaScript 能够察觉到,并随之将用户登录信息发送到后台。从表面上看,dojo.connect 就是一个单纯的函数,完成单纯的事件关联功能。但由于支持参数的灵活配置及和其他 Dojo 函数的有机组合,有时候可以造成一些奇妙效果。而探索这些效果并用于解决一些特定的问题,正是技术的魅力所在吧!

这儿很浅显地讨论一些目前能想到的 dojo.connect 特性。也欢迎有兴趣的同志给出更多的应用场景。

服务层:介绍示例应用程序的服务层的设计

本文的服务层采用的是 Spring Fram ework, 关于 Spring 的其他优点这里不再多说,有兴趣的朋友可以到 Spring 社区去查看。直接上代码:

清单 7.Spring 配置文件代码
                                                         

上面在 datasource 使用了 JNDI,学者可以将它替换为自己的 JDBC 连接方式。

数据访问层:介绍示例应用程序的数据访问层的设计

本例中使用 MyBatis 来实现数据访问层的设计。 MyBatis 作为持久层框架,其主要思想是将程序中的大量 SQL 语句剥离出来,配置在配置文件中,实现 SQL 的灵活配置。这样做的好处是将 SQL 与程序代码分离,可以在不修改程序代码的情况下,直接在配置文件中修改 SQL。下面给出本例子中的部分代码并解释其意思。

Dao 接口类 AdminMaintainDao 代码

清单 8. AdminMaintainDao 代码
 public interface AdminMaintainDao { 	 /** 从数据库中查询所有的记录 *  */ 	 public List getAllAdminMaintain(); 	 /** 新增记录 *  */ 	 public void add(AdminMaintain am);  /** 修改记录 *  */ 	 public void update(AdminMaintain am);  /** 删除记录 *  */ 	 public void delete(String id); 	 }

清单 9. 数据映射配置文件 AdminMaintainDaoMapper.xml 的代码
      	               	      	      	      	         	  	    	       	                                    insert into TLD.Admin_Maintain(agentTeam, forum, url,forum_Type,COMMUNITY_UUID)	   values (#{agentTeam}, #{forum}, #{url},#{forumType},#{communityUuid}) 	  	     	   update TLD.Admin_Maintain set agentTeam=#{agentTeam}, forum=#{forum}, 	   url=#{url},forum_Type=#{forumType},COMMUNITY_UUID=#{communityUuid} 	   where maintain_id=#{id} 	  	              delete from TLD.Admin_Maintain where maintain_id = #{id}            

清单 10. 数据库表定义语句
 CREATE TABLE "TLD"."ADMIN_MAINTAIN"  (  "MAINTAIN_ID" BIGINT NOT NULL GENERATED ALWAYS AS IDENTITY (      START WITH +0      INCREMENT BY +1      MINVALUE +0      MAXVALUE +9223372036854775807      NO CYCLE      CACHE 20      NO ORDER ) ,   "AGENTTEAM" CHAR(50) NOT NULL ,   "FORUM" CHAR(50) NOT NULL ,   "URL" CHAR(200) NOT NULL ,   "FORUM_TYPE" INTEGER NOT NULL ,   "COMMUNITY_UUID" CHAR(50) NOT NULL )   		 IN "USERSPACE1" ;

下面对上面的 dao 代码进行单元测试:

清单 11.DAO 单元测试代码
 public class TestAdminMaintainDao { 	 private Logger logger = Logger.getLogger(this.getClass()); 	 private ClassPathXmlApplicationContext ctx = \	 new ClassPathXmlApplicationContext("applicationContext.xml"); 	 // 从配置文件中初始化实例     private AdminMaintainDao dao = \     (AdminMaintainDao)ctx.getBean("adminMaintainDao");   /** 测试查询 */  @Test  public void testGetAllAdminMaintain() { 					  List list=dao.getAllAdminMaintain(); 	  for (AdminMaintain am : list) { 			            logger.info(am.getAgentTeam()+" : "+am.getUrl()); 	      //add your code here; 	  } 		  }  /** 测试新增 */  @Test  public void testAdd() { 				  AdminMaintain am=new AdminMaintain(); 	  am.setAgentTeam("C"); 	  am.setCommunityUuid("UUidssss8080-980sf"); 	  am.setForum("C Awards"); 	  am.setUrl("http:// c wards"); 	  am.setForumType(1); 	  dao.add(am); 	    } 	 /** 测试修改 */  @Test  public void testUpdate() { 			  AdminMaintain am=new AdminMaintain(); 	  am.setId(11); 	  am.setAgentTeam("D"); 	  am.setCommunityUuid("sfs080-sfs");       am.setForum("D FAQ"); 	  am.setUrl("http:// D FAQ"); 	  am.setForumType(3); 	  dao.update(am); 	    } 	 /** 测试删除 */   @Test   public void testDelete() { 		  dao.delete("11");   }  }

测试:测试示例应用程序

登录到编辑页面,EnhancedGrid 就会从后台把记录列出来,点击“新增”按钮,就会新增一条空的记录。编辑该记录,编辑完后数据就自动的保存到后台数据库,选中所要删除的记录 ( 可以多选 ),点击删除按钮,确认删除后会把所选的记录全部删除,整个过程不用刷新页面就能完成,感觉就像在操作 Excel 表格一样 ( 如图 1),用户交互性很强。

结束语

EnhancedGrid 是一个功能强大、丰富的表格控件,提供伸缩式的表格数据渲染,还包括分页、排序、过滤、列移动、选择、编辑、键盘导航,导出和打印功能等等。 EnhancedGrid 与 store 的紧密联系使得在 Grid 中对数据处理进行配置非常容易,通过与 dojo.connect 在一起使用可以很容易的来绑定自定义函数来实现对表格数据的异步存储。

设计概要:介绍示例应用程序的应用和体系结构

业务场景:

客户需要做一个公司信息管理界面,管理员进入后只要点击查询按钮就能查出所需要的信息,在查询的过程中页面不需要刷新,减少用户的等待时间。

前台框架设计:struts 2.0+Dojo 1.7.1

业务逻辑:Spring 3.1

持久层:mybatis 3.0.6 + DB2 9.7

展现层:介绍示例应用程序的展现层的设计

Dojo EnhancedGrid 简介

顾名思义,EnhancedGrid 就是 Grid 的加强版,从 Dojo 1.4 开始就有了。它的存在是由于原先的 DataGrid 虽然功能强大,但很多地方写得比较死,不太便于扩展。因此,继承自 DataGrid 的 EnhancedGrid 就提供了一种较为灵活的插件机制,一方面能使后来的开发者较少受到现有代码的制约;另一方面也是为功能日益繁多的 Grid 瘦身(不用的插件可以不加载)。 EnhancedGrid 提供了如下一些新的插件功能:

  • Nested Sorting – 多行排序。
  • Indirect Selection – 通过单选按钮和复选按钮选择行。
  • Filter – 支持自定义类型规则来过滤表格的内容。
  • Exporter – 把表格内容导出到各种内容。
  • DnD – Drag-and-drop( 拖放 ) 支持行 / 列 / 表格单元,在单元内外都可以。
  • Pagination – 分页功能。
  • CellMerge – 合并相邻的表格单元到一行中。
  • Search – 通过正则表达式和字符串匹配的方式来查找表格中的内容。

EnhancedGrid 很像 client/server 架构,基本上一个 grid 就是一个 Excel spreadsheet, 通常它被包裹在一个大的表格里面,借于 HTML 的规范,它用自己的形式来展示表格。具体展现形式如下:

图 1. EnhancedGrid 例图

图 1. EnhancedGrid 例图

属性简介:

Dojo EnhancedGrid 有很多属性,这里仅介绍常用的几个属性:

表 1. EnhancedGrid 常用属性介绍
属性 解释
field 数据集里面列的名字
width 列的宽度
editable 能否编辑 :true – 能,false – 不能
formatter 可以改变表格单元值的函数

如何使用 Dojo EnhancedGrid

引入 Dojo EnhancedGrid 控件

要使用 Dojo EnhancedGrid 控件,首先要把此控件加入页面 :

清单 1. 在 JS 文件中引入 Dojo 代码
 require(['dojo/_base/array', 'dojo/_base/lang', 'dojo/_base/event',  'dojo/on', 'dojox/grid/EnhancedGrid', 'dojo/data/ItemFileWriteStore',  'dijit/form/Button', 'dojo/dom', 'dojo/parser', 'dojo/domReady!'],   function(array, lang, event, on, EnhancedGrid, ItemFileWriteStore,           Button, dom, parser){  parser.parse();}

声明表格的标头

清单 2. 在 Dojo EnhancedGrid 中使用 JSON 定义表头
 // 定义 layout 布局,就是定义标题的列标题,宽度 var layout = [[       {'name': 'Id', 'field': 'id', 'width': '3%', editable: true},       {'name': 'Agent Team', 'field': 'agentTeam', 'width': '8%', editable: true},       {'name': 'Community UUid', 'field': 'communityUuid', 'width': '17%', \       editable: true},       {'name': 'View members', 'field': 'communityUuid', 'width': '10%',\       formatter: formatHref},      {'name': 'Forum name', 'field': 'forum', 'width': '10%', editable: true},       {'name': 'Forum Type', 'field': 'forumType', 'width': '6%', editable: true,\       type: dojox.grid.cells.Select, options:["Awards","Program","FAQ","HelpDesk",\       "Best Practices"], values: [ '1', '2','3','4','5' ],formatter: formatForumType},  	  {'name': 'URL', 'field': 'url', 'width': 'auto', editable: true}  ]];

创建 EnhancedGrid 控件对象

清单 3. 创建 EnhancedGrid 控件对象
 grid = new EnhancedGrid({         id: 'grid',         store: store,// 连接 datastore 控件        structure: layout,         rowSelector: '20px'});    /* 把 grid 控件加入到 HTML div 标签中 */    grid.placeAt("gridDiv");  );

下面来自定义一个新增函数来实现异步保存数据到数据库中:

清单 4. 自定义函数来实现异步添加数据
 function onNew(item) {    // 得到表格单元的内容     var agentTeam=item.agentTeam; 	 var communityUuid=item.communityUuid;      var forum=item.forum; 	 var url=item.url; 	 var forumType=item.forumType; 		 dojo.xhrPost({        //The URL of the request        url: "/yourproject/new",        // 要处理内容的格式       handleAs: "json",        // 请求参数      content: { 				        agentTeam: agentTeam,         communityUuid: communityUuid, 	        forum: forum,         url: url,         forumType: forumType        },           // 错误处理函数      error: function(error, ioArgs) {          alert(error.message); 		    } 	   }); 	 // 添加记录后自动刷新页面 ;  setTimeout(function() {window.location.reload();},2000);  }

dojo.xhrPost 是使用 RESTFUL POST 的方式来添加新数据。

添加新增和删除按钮:

清单 5. 新增按钮 HTML 代码
           Add One Row                   Remove Selected Rows     

将函数绑定到 EnhancedGrid 事件上

最后将自定义函数绑定到 EnhancedGrid 事件上:

清单 6. 绑定事件函数代码
 grid = new EnhancedGrid({         id: 'grid',         store: store,// 连接 datastore 控件        structure: layout,         rowSelector: '20px'});/    /* 把 grid 控件加入到 HTML div 标签中 */    grid.placeAt("gridDiv");  // 以下为绑定函数到特定的事件 // 绑定函数到新增功能	 dojo.connect(store, "onNew", this,onNew);  // 绑定函数到修改功能	 dojo.connect(store, "onSet", this,onSet);  // 绑定函数到删除功能	 dojo.connect(store, "onDelete", this,onDelete);     /* 为 button2 按钮添加 click 处理事件 */     on(button2,'click',        function(e){         /* set the properties for the new item: */         var myNewItem = {id: '', agentTeam: 'new',communityUuid:'new',forum: 'new', \        url: 'new', forumType: 0};         /* Insert the new item into the store:*/         store.newItem(myNewItem);// 新增行    }     );  /* 为 button1 按钮添加 click 处理事件 */    on(button1,'click',     function(e){  /* 选择被选中的行 */         var items = grid.selection.getSelected(); 		        if(items.length){            if(!confirm("Are you sure to delete?")){               return false; 					 }             /* 循环遍历所选择的行 */             array.forEach(items, function(selectedItem){                 if(selectedItem !== null){                     /* Delete the item from the data store: */                     store.deleteItem(selectedItem);                 } /* end if */             }); /* end forEach */         }else{            alert("Please select row(s)");  }         event.stop(e);     }  );    

dojo.connect 模型

此处用到了 dojo.connect 模型:dojo.connect(store, "onNew", this,onNew);

只要用户出发了 onNew 事件 ( 创建一条新记录 ),就会触发 onNew 函数(用户可以自己定义)。

每个流行的工具包中,总有一些异常出彩的闪光点。 dojo.connect 就是 Dojo 工具包中,与 JavaScript 事件机制相关的重磅功能。

在 JavaScript 的使用场景中,我们经常需要侦听某些事件的触发,然后进行相应的(函数)处理。比如最常见的,当点击登录页面的登录节点时,JavaScript 能够察觉到,并随之将用户登录信息发送到后台。从表面上看,dojo.connect 就是一个单纯的函数,完成单纯的事件关联功能。但由于支持参数的灵活配置及和其他 Dojo 函数的有机组合,有时候可以造成一些奇妙效果。而探索这些效果并用于解决一些特定的问题,正是技术的魅力所在吧!

这儿很浅显地讨论一些目前能想到的 dojo.connect 特性。也欢迎有兴趣的同志给出更多的应用场景。

服务层:介绍示例应用程序的服务层的设计

本文的服务层采用的是 Spring Framework , 关于 Spring 的其他优点这里不再多说,有兴趣的朋友可以到 Spring 社区去查看。直接上代码:

清单 7.Spring 配置文件代码
                                                         

上面在 datasource 使用了 JNDI,学者可以将它替换为自己的 JDBC 连接方式。

数据访问层:介绍示例应用程序的数据访问层的设计

本例中使用 MyBatis 来实现数据访问层的design. MyBatis 作为持久层框架,其主要思想是将程序中的大量 SQL 语句剥离出来,配置在配置文件中,实现 SQL 的灵活配置。这样做的好处是将 SQL 与程序代码分离,可以在不修改程序代码的情况下,直接在配置文件中修改 SQL。下面给出本例子中的部分代码并解释其意思。

Dao 接口类 AdminMaintainDao 代码

清单 8. AdminMaintainDao 代码
 public interface AdminMaintainDao { 	 /** 从数据库中查询所有的记录 *  */ 	 public List getAllAdminMaintain(); 	 /** 新增记录 *  */ 	 public void add(AdminMaintain am);  /** 修改记录 *  */ 	 public void update(AdminMaintain am);  /** 删除记录 *  */ 	 public void delete(String id); 	 }

清单 9. 数据映射配置文件 AdminMaintainDaoMapper.xml 的代码
      	               	      	      	      	         	  	    	       	                                    insert into TLD.Admin_Maintain(agentTeam, forum, url,forum_Type,COMMUNITY_UUID)	   values (#{agentTeam}, #{forum}, #{url},#{forumType},#{communityUuid}) 	  	     	   update TLD.Admin_Maintain set agentTeam=#{agentTeam}, forum=#{forum}, 	   url=#{url},forum_Type=#{forumType},COMMUNITY_UUID=#{communityUuid} 	   where maintain_id=#{id} 	  	              delete from TLD.Admin_Maintain where maintain_id = #{id}            

清单 10. 数据库表定义语句
 CREATE TABLE "TLD"."ADMIN_MAINTAIN"  (  "MAINTAIN_ID" BIGINT NOT NULL GENERATED ALWAYS AS IDENTITY (      START WITH +0      INCREMENT BY +1      MINVALUE +0      MAXVALUE +9223372036854775807      NO CYCLE      CACHE 20      NO ORDER ) ,   "AGENTTEAM" CHAR(50) NOT NULL ,   "FORUM" CHAR(50) NOT NULL ,   "URL" CHAR(200) NOT NULL ,   "FORUM_TYPE" INTEGER NOT NULL ,   "COMMUNITY_UUID" CHAR(50) NOT NULL )   		 IN "USERSPACE1" ;

下面对上面的 dao 代码进行单元测试:

清单 11.DAO 单元测试代码
 public class TestAdminMaintainDao { 	 private Logger logger = Logger.getLogger(this.getClass()); 	 private ClassPathXmlApplicationContext ctx = \	 new ClassPathXmlApplicationContext("applicationContext.xml"); 	 // 从配置文件中初始化实例     private AdminMaintainDao dao = \     (AdminM aintainDao)ctx.getBean("adminMaintainDao");   /** 测试查询 */  @Test  public void testGetAllAdminMaintain() { 					  List list=dao.getAllAdminMaintain(); 	  for (AdminMaintain am : list) { 			            logger.info(am.getAgentTeam()+" : "+am.getUrl()); 	      //add your code here; 	  } 		  }  /** 测试新增 */  @Test  public void testAdd() { 				  AdminMaintain am=new AdminMaintain(); 	  am.setAgentTeam("C"); 	  am.setCommunityUuid("UUidssss8080-980sf"); 	  am.setForum("C Awards"); 	  am.setUrl("http:// c wards"); 	  am.setForumType(1); 	  dao.add(am); 	    } 	 /** 测试修改 */  @Test  public void testUpdate() { 			  AdminMaintain am=new AdminMaintain(); 	  am.setId(11); 	  am.setAgentTeam("D"); 	  am.setCommunityUuid("sfs080-sfs");       am.setForum("D FAQ"); 	  am.setUrl("http:// D FAQ"); 	  am.setForumType(3); 	  dao.update(am); 	    } 	 /** 测试删除 */   @Test   public void testDelete() { 		  dao.delete("11");   }  }

测试:测试示例应用程序

登录到编辑页面,EnhancedGrid 就会从后台把记录列出来,点击“新增”按钮,就会新增一条空的记录。编辑该记录,编辑完后数据就自动的保存到后台数据库,选中所要删除的记录 ( 可以多选 ),点击删除按钮,确认删除后会把所选的记录全部删除,整个过程不用刷新页面就能完成,感觉就像在操作 Excel 表格一样 ( 如图 1),用户交互性很强。

结束语

EnhancedGrid 是一个功能强大、丰富的表格控件,提供伸缩式的表格数据渲染,还包括分页、排序、过滤、列移动、选择、编辑、键盘导航,导出和打印功能等等。 EnhancedGrid 与 store 的紧密联系使得在 Grid 中对数据处理进行配置非常容易,通过与 dojo.connect 在一起使用可以很容易的来绑定自定义函数来实现对表格数据的异步存储。

设计概要:介绍示例应用程序的应用和体系结构

业务场景:

客户需要做一个公司信息管理界面,管理员进入后只要点击查询按钮就能查出所需要的信息,在查询的过程中页面不需要刷新,减少用户的等待时间。

前台框架设计:struts 2.0+Dojo 1.7.1

业务逻辑:Spring 3.1

持久层:mybatis 3.0.6 + DB2 9.7

展现层:介绍示例应用程序的展现层的设计

Dojo EnhancedGrid 简介

顾名思义,EnhancedGrid 就是 Grid 的加强版,从 Dojo 1.4 开始就有了。它的存在是由于原先的 DataGrid 虽然功能强大,但很多地方写得比较死,不太便于扩展。因此,继承自 DataGrid 的 EnhancedGrid 就提供了一种较为灵活的插件机制,一方面能使后来的开发者较少受到现有代码的制约;另一方面也是为功能日益繁多的 Grid 瘦身(不用的插件可以不加载)。 EnhancedGrid 提供了如下一些新的插件功能:

  • Nested Sorting – 多行排序。
  • Indirect Selection – 通过单选按钮和复选按钮选择行。
  • Filter – 支持自定义类型规则来过滤表格的内容。
  • Exporter – 把表格内容导出到各种内容。
  • DnD – Drag-and-drop( 拖放 ) 支持行 / 列 / 表格单元,在单元内外都可以。
  • Pagination – 分页功能。
  • CellMerge – 合并相邻的表格单元到一行中。
  • Search – 通过正则表达式和字符串匹配的方式来查找表格中的内容。

EnhancedGrid 很像 client/server 架构,基本上一个 grid 就是一个 Excel spreadsheet, 通常它被包裹在一个大的表格里面,借于 HTML 的规范,它用自己的形式来展示表格。具体展现形式如下:

图 1. EnhancedGrid 例图

图 1. EnhancedGrid 例图

属性简介:

Dojo EnhancedGrid 有很多属性,这里仅介绍常用的几个属性:

表 1. EnhancedGrid 常用属性介绍

属性 解释
field 数据集里面列的名字
width 列的宽度
editable 能否编辑 :true - 能,false - 不能
formatter 可以改变表格单元值的函数

如何使用 Dojo EnhancedGrid

引入 Dojo EnhancedGrid 控件

要使用 Dojo EnhancedGrid 控件,首先要把此控件加入页面 :

清单 1. 在 JS 文件中引入 Dojo 代码
 require(['dojo/_base/array', 'dojo/_base/lang', 'dojo/_base/event',  'dojo/on', 'dojox/grid/EnhancedGrid', 'dojo/data/ItemFileWriteStore',  'dijit/form/Button', 'dojo/dom', 'dojo/parser', 'dojo/domReady!'],   function(array, lang, event, on, EnhancedGrid, ItemFileWriteStore,           Button, dom, parser){  parser.parse();}

声明表格的标头

清单 2. 在 Dojo EnhancedGrid 中使用 JSON 定义表头
 // 定义 layout 布局,就是定义标题的列标题,宽度 var layout = [[       {'name': 'Id', 'field': 'id', 'width': '3%', editable: true},       {'name': 'Agent Team', 'field': 'agentTeam', 'width': '8%', editable: true},       {'name': 'Community UUid', 'field': 'communityUuid', 'width': '17%', \       editable: true},       {'n ame': 'View members', 'field': 'communityUuid', 'width': '10%',\       formatter: formatHref},      {'name': 'Forum name', 'field': 'forum', 'width': '10%', editable: true},       {'name': 'Forum Type', 'field': 'forumType', 'width': '6%', editable: true,\       type: dojox.grid.cells.Select, options:["Awards","Program","FAQ","HelpDesk",\       "Best Practices"], values: [ '1', '2','3','4','5' ],formatter: formatForumType},  	  {'name': 'URL', 'field': 'url', 'width': 'auto', editable: true}  ]];

创建 EnhancedGrid 控件对象

清单 3. 创建 EnhancedGrid 控件对象
 grid = new EnhancedGrid({         id: 'grid',         store: store,// 连接 datastore 控件        structure: layout,         rowSelector: '20px'});    /* 把 grid 控件加入到 HTML div 标签中 */    grid.placeAt("gridDiv");  );

下面来自定义一个新增函数来实现异步保存数据到数据库中:

清单 4. 自定义函数来实现异步添加数据
 function onNew(item) {    // 得到表格单元的内容     var agentTeam=item.agentTeam; 	 var communityUuid=item.communityUuid;      var forum=item.forum; 	 var url=item.url; 	 var forumType=item.forumType; 		 dojo.xhrPost({        //The URL of the request        url: "/yourproject/new",        // 要处理内容的格式       handleAs: "json",        // 请求参数      content: { 				        agentTeam: agentTeam,         communityUuid: communityUuid, 	        forum: forum,         url: url,         forumType: forumType        },           // 错误处理函数      error: function(error, ioArgs) {          alert(error.message); 		    } 	   }); 	 // 添加记录后自动刷新页面 ;  setTimeout(function() {window.location.reload();},2000);  }

dojo.xhrPost 是使用 RESTFUL POST 的方式来添加新数据。

添加新增和删除按钮:

清单 5. 新增按钮 HTML 代码
 < ! —添加新增按钮 -  

Add One Row      Remove Selected Rows

将函数绑定到 EnhancedGrid 事件上

最后将自定义函数绑定到 EnhancedGrid 事件上:

清单 6. 绑定事件函数代码
 grid = new EnhancedGrid({         id: 'grid',         store: store,// 连接 datastore 控件        structure: layout,         rowSelector: '20px'});/    /* 把 grid 控件加入到 HTML div 标签中 */    grid.placeAt("gridDiv");  // 以下为绑定函数到特定的事件 // 绑定函数到新增功能	 dojo.connect(store, "onNew", this,onNew);  // 绑定函数到修改功能	 dojo.connect(store, "onSet", this,onSet);  // 绑定函数到删除功能	 dojo.connect(store, "onDelete", this,onDelete);     /* 为 button2 按钮添加 click 处理事件 */     on(button2,'click',        function(e){         /* set the properties for the new item: */         var myNewItem = {id: '', agentTeam: 'new',communityUuid:'new',forum: 'new', \        url: 'new', forumType: 0};         /* Insert the new item into the store:*/         store.newItem(myNewItem);// 新增行    }     );  /* 为 button1 按钮添加 click 处理事件 */    on(button1,'click',     function(e){  /* 选择被选中的行 */         var items = grid.selection.getSelected(); 		        if(items.length){            if(!confirm("Are you sure to delete?")){               return false; 					 }             /* 循环遍历所选择的行 */             array.forEach(items, function(selectedItem){                 if(selectedItem !== null){                     /* Delete the item from the data store: */                     store.deleteItem(selectedItem);                 } /* end if */             }); /* end forEach */         }else{            alert("Please select row(s)");  }         event.stop(e);     }  );

dojo.connect 模型

此处用到了 dojo.connect 模型:dojo.connect(store, "onNew", this,onNew);

只要用户出发了 onNew 事件 ( 创建一条新记录 ),就会触发 onNew 函数(用户可以自己定义)。

每个流行的工具包中,总有一些异常出彩的闪光点。 dojo.connect 就是 Dojo 工具包中,与 JavaScript 事件机制相关的重磅功能。

在 JavaScript 的使用场景中,我们经常需要侦听某些事件的触发,然后进行相应的(函数)处理。比如最常见的,当点击登录页面的登录节点时,JavaScript 能够察觉到,并随之将用户登录信息发送到后台。从表面上看,dojo.connect 就是一个单纯的函数,完成单纯的事件关联功能。但由于支持参数的灵活配置及和其他 Dojo 函数的有机组合,有时候可以造成一些奇妙效果。而探索这些效果并用于解决一些特定的问题,正是技术的魅力所在吧!

这儿很浅显地讨论一些目前能想到的 dojo.connect 特性。也欢迎有兴趣的同志给出更多的应用场景。

服务层:介绍示例应用程序的服务层的设计

本文的服务层采用的是 Spring Framework, 关于 Spring 的其他优点这里不再多说,有兴趣的朋友可以到 Spring 社区去查看。直接上代码:

清单 7.Spring 配置文件代码
                                                         

上面在 datasource 使用了 JNDI,学者可以将它替换为自己的 JDBC 连接方式。

数据访问层:介绍示例应用程序的数据访问层的设计

本例中使用 MyBatis 来实现数据访问层的设计。 MyBatis 作为持久层框架,其主要思想是将程序中的大量 SQL 语句剥离出来,配置在配置文件中,实现 SQL 的灵活配置。这样做的好处是将 SQL 与程序代码分离,可以在不修改程序代码的情况下,直接在配置文件中修改 SQL。下面给出本例子中的部分代码并解释其意思。

Dao 接口类 AdminMaintainDao 代码

清单 8. AdminMaintainDao 代码
 public interface AdminMaintainDao { 	 /** 从数据库中查询所有的记录 *  */ 	 public List get AllAdminMaintain(); 	 /** 新增记录 *  */ 	 public void add(AdminMaintain am);  /** 修改记录 *  */ 	 public void update(AdminMaintain am);  /** 删除记录 *  */ 	 public void delete(String id); 	 }

清单 9. 数据映射配置文件 AdminMaintainDaoMapper.xml 的代码
      	               	      	      	      	         	  	    	       	                                    insert into TLD.Admin_Maintain(agentTeam, forum, url,forum_Type,COMMUNITY_UUID)	   values (#{agentTeam}, #{forum}, #{url},#{forumType},#{communityUuid}) 	  	     	   update TLD.Admin_Maintain set agentTeam=#{agentTeam}, forum=#{forum}, 	   url=#{url},forum_Type=#{forumType},COMMUNITY_UUID=#{communityUuid} 	   where maintain_id=#{id} 	  	              delete from TLD.Admin_Maintain where maintain_id = #{id}            

清单 10. 数据库表定义语句
 CREATE TABLE "TLD"."ADMIN_MAINTAIN"  (  "MAINTAIN_ID" BIGINT NOT NULL GENERATED ALWAYS AS IDENTITY (      START WITH +0      INCREMENT BY +1      MINVALUE +0      MAXVALUE +9223372036854775807      NO CYCLE      CACHE 20      NO ORDER ) ,   "AGENTTEAM" CHAR(50) NOT NULL ,   "FORUM" CHAR(50) NOT NULL ,   "URL" CHAR(200) NOT NULL ,   "FORUM_TYPE" INTEGER NOT NULL ,   "COMMUNITY_UUID" CHAR(50) NOT NULL )   		 IN "USERSPACE1" ;

下面对上面的 dao 代码进行单元测试:

清单 11.DAO 单元测试代码
 public class TestAdminMaintainDao { 	 private Logger logger = Logger.getLogger(this.getClass()); 	 private ClassPathXmlApplicationContext ctx = \	 new ClassPathXmlApplicationContext("applicationContext.xml"); 	 // 从配置文件中初始化实例     private AdminMaintainDao dao = \     (AdminMaintainDao)ctx.getB ean("adminMaintainDao");   /** 测试查询 */  @Test  public void testGetAllAdminMaintain() { 					  List list=dao.getAllAdminMaintain(); 	  for (AdminMaintain am : list) { 			            logger.info(am.getAgentTeam()+" : "+am.getUrl()); 	      //add your code here; 	  } 		  }  /** 测试新增 */  @Test  public void testAdd() { 				  AdminMaintain am=new AdminMaintain(); 	  am.setAgentTeam("C"); 	  am.setCommunityUuid("UUidssss8080-980sf"); 	  am.setForum("C Awards"); 	  am.setUrl("http:// c wards"); 	  am.setForumType(1); 	  dao.add(am); 	    } 	 /** 测试修改 */  @Test  public void testUpdate() { 			  AdminMaintain am=new AdminMaintain(); 	  am.setId(11); 	  am.setAgentTeam("D"); 	  am.setCommunityUuid("sfs080-sfs");       am.setForum("D FAQ"); 	  am.setUrl("http:// D FAQ"); 	  am.setForumType(3); 	  dao.update(am); 	    } 	 /** 测试删除 */   @Test   public void testDelete() { 		  dao.delete("11");   }  }

测试:测试示例应用程序

登录到编辑页面,EnhancedGrid 就会从后台把记录列出来,点击“新增”按钮,就会新增一条空的记录。编辑该记录,编辑完后数据就自动的保存到后台数据库,选中所要删除的记录 ( 可以多选 ),点击删除按钮,确认删除后会把所选的记录全部删除,整个过程不用刷新页面就能完成,感觉就像在操作 Excel 表格一样 ( 如图 1),用户交互性很强。

结束语

EnhancedGrid 是一个功能强大、丰富的表格控件,提供伸缩式的表格数据渲染,还包括分页、排序、过滤、列移动、选择、编辑、键盘导航,导出和打印功能等等。 EnhancedGrid 与 store 的紧密联系使得在 Grid 中对数据处理进行配置非常容易,通过与 dojo.connect 在一起使用可以很容易的来绑定自定义函数来实现对表格数据的异步存储。

设计概要:介绍示例应用程序的应用和体系结构

业务场景:

客户需要做一个公司信息管理界面,管理员进入后只要点击查询按钮就能查出所需要的信息,在查询的过程中页面不需要刷新,减少用户的等待时间。

前台框架设计:struts 2.0+Dojo 1.7.1

业务逻辑:Spring 3.1

持久层:mybatis 3.0.6 + DB2 9.7

展现层:介绍示例应用程序的展现层的设计

Dojo EnhancedGrid 简介

顾名思义,EnhancedGrid 就是 Grid 的加强版,从 Dojo 1.4 开始就有了。它的存在是由于原先的 DataGrid 虽然功能强大,但很多地方写得比较死,不太便于扩展。因此,继承自 DataGrid 的 EnhancedGrid 就提供了一种较为灵活的插件机制,一方面能使后来的开发者较少受到现有代码的制约;另一方面也是为功能日益繁多的 Grid 瘦身(不用的插件可以不加载)。 EnhancedGrid 提供了如下一些新的插件功能:

  • Nested Sorting – 多行排序。
  • Indirect Selection – 通过单选按钮和复选按钮选择行。
  • Filter – 支持自定义类型规则来过滤表格的内容。
  • Exporter – 把表格内容导出到各种内容。
  • DnD – Drag-and-drop( 拖放 ) 支持行 / 列 / 表格单元,在单元内外都可以。
  • Pagination – 分页功能。
  • CellMerge – 合并相邻的表格单元到一行中。
  • Search – 通过正则表达式和字符串匹配的方式来查找表格中的内容。

EnhancedGrid 很像 client/server 架构,基本上一个 grid 就是一个 Excel spreadsheet, 通常它被包裹在一个大的表格里面,借于 HTML 的规范,它用自己的形式来展示表格。具体展现形式如下:

图 1. EnhancedGrid 例图

图 1. EnhancedGrid 例图

属性简介:

Dojo EnhancedGrid 有很多属性,这里仅介绍常用的几个属性:

表 1. EnhancedGrid 常用属性介绍
属性 解释
field 数据集里面列的名字
width 列的宽度
editable 能否编辑 :true - 能,false - 不能
formatter 可以改变表格单元值的函数

如何使用 Dojo EnhancedGrid

引入 Dojo EnhancedGrid 控件

要使用 Dojo EnhancedGrid 控件,首先要把此控件加入页面 :

清单 1. 在 JS 文件中引入 Dojo 代码
 require(['dojo/_base/array', 'dojo/_base/lang', 'dojo/_base/event',  'dojo/on', 'dojox/grid/EnhancedGrid', 'dojo/data/ItemFileWriteStore',  'dijit/form/Button', 'dojo/dom', 'dojo/parser', 'dojo/domReady!'],   function(array, lang, event, on, EnhancedGrid, ItemFileWriteStore,           Button, dom, parser){  parser.parse();}

声明表格的标头

清单 2. 在 Dojo EnhancedGrid 中使用 JSON 定义表头
 // 定义 layout 布局,就是定义标题的列标题,宽度 var layout = [[       {'name': 'Id', 'field': 'id', 'width': '3%', editable: true},       {'name': 'Agent Team', 'field': 'agentTeam', 'width': '8%', editable: true},       {'name': 'Community UUid', 'field': 'communityUuid', 'width': '17%', \       editable: true},       {'name': 'View members', 'field': 'communityU uid', 'width': '10%',\       formatter: formatHref},      {'name': 'Forum name', 'field': 'forum', 'width': '10%', editable: true},       {'name': 'Forum Type', 'field': 'forumType', 'width': '6%', editable: true,\       type: dojox.grid.cells.Select, options:["Awards","Program","FAQ","HelpDesk",\       "Best Practices"], values: [ '1', '2','3','4','5' ],formatter: formatForumType},  	  {'name': 'URL', 'field': 'url', 'width': 'auto', editable: true}  ]];

创建 EnhancedGrid 控件对象

清单 3. 创建 EnhancedGrid 控件对象
 grid = new EnhancedGrid({         id: 'grid',         store: store,// 连接 datastore 控件        structure: layout,         rowSelector: '20px'});    /* 把 grid 控件加入到 HTML div 标签中 */    grid.placeAt("gridDiv");  );

下面来自定义一个新增函数来实现异步保存数据到数据库中:

清单 4. 自定义函数来实现异步添加数据
 function onNew(item) {    // 得到表格单元的内容     var agentTeam=item.agentTeam; 	 var communityUuid=item.communityUuid;      var forum=item.forum; 	 var url=item.url; 	 var forumType=item.forumType; 		 dojo.xhrPost({        //The URL of the request        url: "/yourproject/new",        // 要处理内容的格式       handleAs: "json",        // 请求参数      content: { 				        agentTeam: agentTeam,         communityUu id: communityUuid, 	        forum: forum,         url: url,         forumType: forumType        },           // 错误处理函数      error: function(error, ioArgs) {          alert(error.message); 		    } 	   }); 	 // 添加记录后自动刷新页面 ;  setTimeout(function() {window.location.reload();},2000);  }

dojo.xhrPost 是使用 RESTFUL POST 的方式来添加新数据。

添加新增和删除按钮:

清单 5. 新增按钮 HTML 代码
           Add One Row                   Remove Selected Rows     

将函数绑定到 EnhancedGrid 事件上

最后将自定义函数绑定到 EnhancedGrid 事件上:

清单 6. 绑定事件函数代码
 grid = new EnhancedGrid({         id: 'grid',         store: store,// 连接 datastore 控件        structure: layout,         rowSelector: '20px'});/    /* 把 grid 控件加入到 HTML div 标签中 */    grid.placeAt("gridDiv");  // 以下为绑定函数到特定的事件 // 绑定函数到新增功能	 dojo.connect(store, "onNew", this,onNew);  // 绑定函数到修改功能	 dojo.connect(store, "onSet", this,onSet);  // 绑定函数到删除功能	 dojo.connect(store, "onDelete", this,onDelete);     /* 为 button2 按钮添加 click 处理事件 */     on(button2,'click',        function(e){         /* set the properties for the new item: */         var myNewItem = {id: '', agentTeam: 'new',communityUuid:'new',forum: 'new', \        url: 'new', forumType: 0};         /* Insert the new item into the store:*/         store.newItem(myNewItem);// 新增行    }     );  /* 为 button1 按钮添加 click 处理事件 */    on(button1,'click',     function(e){  /* 选择被选中的行 */         var items = grid.selection.getSelected(); 		        if(items.length){            if(!confirm("Are you sure to delete?")){               return false; 					 }             /* 循环遍历所选择的行 */             array.forEach(items, function(selectedItem){                 if(selectedItem !== null){                     /* Delete the item from the data store: */                     store.deleteItem(selectedItem);                 } /* end if */             }); /* end forEach */         }else{            alert("Please select row(s)");  }         event.stop(e);     }  );

dojo.connect 模型

此处用到了 dojo.connect 模型:dojo.connect(store, "onNew", this,onNew);

只要用户出发了 onNew 事件 ( 创建一条新记录 ),就会触发 onNew 函数(用户可以自己定义)。

每个流行的工具包中,总有一些异常出彩的闪光点。 dojo.connect 就是 Dojo 工具包中,与 JavaScript 事件机制相关的重磅功能。

在 JavaScript 的使用场景中,我们经常需要侦听某些事件的触发,然后进行相应的(函数)处理。比如最常见的,当点击登录页面的登录节点时,JavaScript 能够察觉到,并随之将用户登录信息发送到后台。从表面上看,dojo.connect 就是一个单纯的函数,完成单纯的事件关联功能。但由于支持参数的灵活配置及和其他 Dojo 函数的有机组合,有时候可以造成一些奇妙效果。而探索这些效果并用于解决一些特定的问题,正是技术的魅力所在吧!

这儿很浅显地讨论一些目前能想到的 dojo.connect 特性。也欢迎有兴趣的同志给出更多的应用场景。

服务层:介绍示例应用程序的服务层的设计

本文的服务层采用的是 Spring Framework, 关于 Sprin g 的其他优点这里不再多说,有兴趣的朋友可以到 Spring 社区去查看。直接上代码:

清单 7.Spring 配置文件代码
                                                         

上面在 datasource 使用了 JNDI,学者可以将它替换为自己的 JDBC 连接方式。

数据访问层:介绍示例应用程序的数据访问层的设计

本例中使用 MyBatis 来实现数据访问层的设计。 MyBatis 作为持久层框架,其主要思想是将程序中的大量 SQL 语句剥离出来,配置在配置文件中,实现 SQL 的灵活配置。这样做的好处是将 SQL 与程序代码分离,可以在不修改程序代码的情况下,直接在配置文件中修改 SQL。下面给出本例子中的部分代码并解释其意思。

Dao 接口类 AdminMaintainDao 代码

清单 8. AdminMaintainDao 代码
 public interface AdminMaintainDao { 	 /** 从数据库中查询所有的记录 *  */ 	 public List getAllA dminMaintain(); 	 /** 新增记录 *  */ 	 public void add(AdminMaintain am);  /** 修改记录 *  */ 	 public void update(AdminMaintain am);  /** 删除记录 *  */ 	 public void delete(String id); 	 }

清单 9. 数据映射配置文件 AdminMaintainDaoMapper.xml 的代码
      	               	      	      	      	         	  	    	       	                                    insert into TLD.Admin_Maintain(agentTeam, forum, url,forum_Type,COMMUNITY_UUID)	   values (#{agentTeam}, #{forum}, #{url},#{forumType},#{communityUuid}) 	  	     	   update TLD.Admin_Maintain set agentTeam=#{agentTeam}, forum=#{forum}, 	   url=#{url},forum_Type=#{forumType},COMMUNITY_UUID=#{communityUuid} 	   where maintain_id=#{id} 	  	              delete from TLD.Admin_Maintain where maintain_id = #{id}            

清单 10. 数据库表定义语句
 CREATE TABLE "TLD"."ADMIN_MAINTAIN"  (  "MAINTAIN_ID" BIGINT NOT NULL GENERATED ALWAYS AS IDENTITY (      START WITH +0      INCREMENT BY +1      MINVALUE +0      MAXVALUE +9223372036854775807      NO CYCLE      CACHE 20      NO ORDER ) ,   "AGENTTEAM" CHAR(50) NOT NULL ,   "FORUM" CHAR(50) NOT NULL ,   "URL" CHAR(200) NOT NULL ,   "FORUM_TYPE" INTEGER NOT NULL ,   "COMMUNITY_UUID" CHAR(50) NOT NULL )   		 IN "USERSPACE1" ;

下面对上面的 dao 代码进行单元测试:

清单 11.DAO 单元测试代码
 public class TestAdminMaintainDao { 	 private Logger logger = Logger.getLogger(this.getClass()); 	 private ClassPathXmlApplicationContext ctx = \	 new ClassPathXmlApplicationContext("applicationContext.xml"); 	 // 从配置文件中初始化实例     private AdminMaintainDao dao = \     (AdminMaintainDao)ctx.getBean("adminMaintainD ao");   /** 测试查询 */  @Test  public void testGetAllAdminMaintain() { 					  List list=dao.getAllAdminMaintain(); 	  for (AdminMaintain am : list) { 			            logger.info(am.getAgentTeam()+" : "+am.getUrl()); 	      //add your code here; 	  } 		  }  /** 测试新增 */  @Test  public void testAdd() { 				  AdminMaintain am=new AdminMaintain(); 	  am.setAgentTeam("C"); 	  am.setCommunityUuid("UUidssss8080-980sf"); 	  am.setForum("C Awards"); 	  am.setUrl("http:// c wards"); 	  am.setForumType(1); 	  dao.add(am); 	    } 	 /** 测试修改 */  @Test  public void testUpdate() { 			  AdminMaintain am=new AdminMaintain(); 	  am.setId(11); 	  am.setAgentTeam("D"); 	  am.setCommunityUuid("sfs080-sfs");       am.setForum("D FAQ"); 	  am.setUrl("http:// D FAQ"); 	  am.setForumType(3); 	  dao.update(am); 	    } 	 /** 测试删除 */   @Test   public void testDelete() { 		  dao.delete("11");   }  }

测试:测试示例应用程序

登录到编辑页面,EnhancedGrid 就会从后台把记录列出来,点击“新增”按钮,就会新增一条空的记录。编辑该记录,编辑完后数据就自动的保存到后台数据库,选中所要删除的记录 ( 可以多选 ),点击删除按钮,确认删除后会把所选的记录全部删除,整个过程不用刷新页面就能完成,感觉就像在操作 Excel 表格一样 ( 如图 1),用户交互性很强。

结束语

EnhancedGrid 是一个功能强大、丰富的表格控件,提供伸缩式的表格数据渲染,还包括分页、排序、过滤、列移动、选择、编辑、键盘导航,导出和打印功能等等。 EnhancedGrid 与 store 的紧密联系使得在 Grid 中对数据处理进行配置非常容易,通过与 dojo.connect 在一起使用可以很容易的来绑定自定义函数来实现对表格数据的异步存储。

设计概要:介绍示例应用程序的应用和体系结构

业务场景:

客户需要做一个公司信息管理界面,管理员进入后只要点击查询按钮就能查出所需要的信息,在查询的过程中页面不需要刷新,减少用户的等待时间。

前台框架设计:struts 2.0+Dojo 1.7.1

业务逻辑:Spring 3.1

持久层:mybatis 3.0.6 + DB2 9.7

展现层:介绍示例应用程序的展现层的设计

Dojo EnhancedGrid 简介

顾名思义,EnhancedGrid 就是 Grid 的加强版,从 Dojo 1.4 开始就有了。它的存在是由于原先的 DataGrid 虽然功能强大,但很多地方写得比较死,不太便于扩展。因此,继承自 DataGrid 的 EnhancedGrid 就提供了一种较为灵活的插件机制,一方面能使后来的开发者较少受到现有代码的制约;另一方面也是为功能日益繁多的 Grid 瘦身(不用的插件可以不加载)。 EnhancedGrid 提供了如下一些新的插件功能:

  • Nested Sorting – 多行排序。
  • Indirect Selection – 通过单选按钮和复选按钮选择行。
  • Filter – 支持自定义类型规则来过滤表格的内容。
  • Exporter – 把表格内容导出到各种内容。
  • DnD – Drag-and-drop( 拖放 ) 支持行 / 列 / 表格单元,在单元内外都可以。
  • Pagination – 分页功能。
  • CellMerge – 合并相邻的表格单元到一行中。
  • Search – 通过正则表达式和字符串匹配的方式来查找表格中的内容。

EnhancedGrid 很像 client/server 架构,基本上一个 grid 就是一个 Excel spreadsheet, 通常它被包裹在一个大的表格里面,借于 HTML 的规范,它用自己的形式来展示表格。具体展现形式如下:

图 1. EnhancedGrid 例图

图 1. EnhancedGrid 例图

属性简介:

Dojo EnhancedGrid 有很多属性,这里仅介绍常用的几个属性:

表 1. EnhancedGrid 常用属性介绍
属性 解释
field 数据集里面列的名字
width 列的宽度
editable 能否编辑 :true - 能,false - 不能
formatter 可以改变表格单元值的函数

如何使用 Dojo EnhancedGrid

引入 Dojo EnhancedGrid 控件

要使用 Dojo EnhancedGrid 控件,首先要把此控件加入页面 :

清单 1. 在 JS 文件中引入 Dojo 代码
 require(['dojo/_base/array', 'dojo/_base/lang', 'dojo/_base/event',  'dojo/on', 'dojox/grid/EnhancedGrid', 'dojo/data/ItemFileWriteStore',  'dijit/form/Button', 'dojo/dom', 'dojo/parser', 'dojo/domReady!'],   function(array, lang, event, on, EnhancedGrid, ItemFileWriteStore,           Button, dom, parser){  parser.parse();}

声明表格的标头

清单 2. 在 Dojo EnhancedGrid 中使用 JSON 定义表头
 // 定义 layout 布局,就是定义标题的列标题,宽度 var layout = [[       {'name': 'Id', 'field': 'id', 'width': '3%', editable: true},       {'name': 'Agent Team', 'field': 'agentTeam', 'width': '8%', editable: true},       {'name': 'Community UUid', 'field': 'communityUuid', 'width': '17%', \       editable: true},       {'name': 'View members', 'field': 'communityUuid', 'width': '10%',\       formatter: fo rmatHref},      {'name': 'Forum name', 'field': 'forum', 'width': '10%', editable: true},       {'name': 'Forum Type', 'field': 'forumType', 'width': '6%', editable: true,\       type: dojox.grid.cells.Select, options:["Awards","Program","FAQ","HelpDesk",\       "Best Practices"], values: [ '1', '2','3','4','5' ],formatter: formatForumType},  	  {'name': 'URL', 'field': 'url', 'width': 'auto', editable: true}  ]];

创建 EnhancedGrid 控件对象

清单 3. 创建 EnhancedGrid 控件对象
 grid = new EnhancedGrid({         id: 'grid',         store: store,// 连接 datastore 控件        structure: layout,         rowSelector: '20px'});    /* 把 grid 控件加入到 HTML div 标签中 */    grid.placeAt("gridDiv");  );

下面来自定义一个新增函数来实现异步保存数据到数据库中:

清单 4. 自定义函数来实现异步添加数据
 function onNew(item) {    // 得到表格单元的内容     var agentTeam=item.agentTeam; 	 var communityUuid=item.communityUuid;      var forum=item.forum; 	 var url=item.url; 	 var forumType=item.forumType; 		 dojo.xhrPost({        //The URL of the request        url: "/yourproject/new",        // 要处理内容的格式       handleAs: "json",        // 请求参数      content: { 				        agentTeam: agentTeam,         communityUuid: communityUuid, 	        forum: forum,         ur l: url,         forumType: forumType        },           // 错误处理函数      error: function(error, ioArgs) {          alert(error.message); 		    } 	   }); 	 // 添加记录后自动刷新页面 ;  setTimeout(function() {window.location.reload();},2000);  }

dojo.xhrPost 是使用 RESTFUL POST 的方式来添加新数据。

添加新增和删除按钮:

清单 5. 新增按钮 HTML 代码
           Add One Row                   Remove Selected Rows     

将函数绑定到 EnhancedGrid 事件上

最后将自定义函数绑定到 EnhancedGrid 事件上:

清单 6. 绑定事件函数代码
 grid = new EnhancedGrid({         id: 'grid',         store: store,// 连接 datastore 控件        structure: layout,         rowSelector: '20px'});/    /* 把 grid 控件加入到 HTML div 标签中 */    grid.placeAt("gridDiv");  // 以下为绑定函数到特定的事件 // 绑定函数到新增功能	 dojo.connect(store, "onNew", this,onNew);  // 绑定函数到修改功能	 dojo.connect(store, "onSet", this,onSet);  // 绑定函数到删除功能	 dojo.connect(store, "onDelete", this,onDelete);     /* 为 button2 按钮添加 click 处理事件 */     on(button2,'click',        function(e){         /* set the properties for the new item: */         var myNewItem = {id: '', agentTeam: 'new',communityUuid:'new',forum: 'new', \        url: 'new', forumType: 0};         /* Insert the new item into the store:*/         store.newItem(myNewItem);// 新增行    }     );  /* 为 button1 按钮添加 click 处理事件 */    on(button1,'click',     function(e){  /* 选择被选中的行 */         var items = grid.selection.getSelected(); 		        if(items.length){            if(!confirm("Are you sure to delete?")){               return false; 					 }             /* 循环遍历所选择的行 */             array.forEach(items, function(selectedItem){                 if(selectedItem !== null){                     /* Delete the item from the data store: */                     store.deleteItem(selectedItem);                 } /* end if */             }); /* end forEach */         }else{            alert("Please select row(s)");  }         event.stop(e);     }  );

dojo.connect 模型

此处用到了 dojo.connect 模型:dojo.connect(store, "onNew", this,onNew);

只要用户出发了 onNew 事件 ( 创建一条新记录 ),就会触发 onNew 函数(用户可以自己定义)。

每个流行的工具包中,总有一些异常出彩的闪光点。 dojo.connect 就是 Dojo 工具包中,与 JavaScript 事件机制相关的重磅功能。

在 JavaScript 的使用场景中,我们经常需要侦听某些事件的触发,然后进行相应的(函数)处理。比如最常见的,当点击登录页面的登录节点时,JavaScript 能够察觉到,并随之将用户登录信息发送到后台。从表面上看,dojo.connect 就是一个单纯的函数,完成单纯的事件关联功能。但由于支持参数的灵活配置及和其他 Dojo 函数的有机组合,有时候可以造成一些奇妙效果。而探索这些效果并用于解决一些特定的问题,正是技术的魅力所在吧!

这儿很浅显地讨论一些目前能想到的 dojo.connect 特性。也欢迎有兴趣的同志给出更多的应用场景。

服务层:介绍示例应用程序的服务层的设计

本文的服务层采用的是 Spring Framework, 关于 Spring 的其他优点这里不再多说,有兴趣的朋友可以到 Spring 社区去查看。直接上代码:

清单 7.Spring 配置文件代码
                                                         

上面在 datasource 使用了 JNDI,学者可以将它替换为自己的 JDBC 连接方式。

数据访问层:介绍示例应用程序的数据访问层的设计

本例中使用 MyBatis 来实现数据访问层的设计。 MyBatis 作为持久层框架,其主要思想是将程序中的大量 SQL 语句剥离出来,配置在配置文件中,实现 SQL 的灵活配置。这样做的好处是将 SQL 与程序代码分离,可以在不修改程序代码的情况下,直接在配置文件中修改 SQL。下面给出本例子中的部分代码并解释其意思。

Dao 接口类 AdminMaintainDao 代码

清单 8. AdminMaintainDao 代码
 public interface AdminMaintainDao { 	 /** 从数据库中查询所有的记录 *  */ 	 public List getAllAdmin Maintain(); 	 /** 新增记录 *  */ 	 public void add(AdminMaintain am);  /** 修改记录 *  */ 	 public void update(AdminMaintain am);  /** 删除记录 *  */ 	 public void delete(String id); 	 }

清单 9. 数据映射配置文件 AdminMaintainDaoMapper.xml 的代码
      	               	      	      	      	         	  	    	       	                                    insert into TLD.Admin_Maintain(agentTeam, forum, url,forum_Type,COMMUNITY_UUID)	   values (#{agentTeam}, #{forum}, #{url},#{forumType},#{communityUuid}) 	  	     	   update TLD.Admin_Maintain set agentTeam=#{agentTeam}, forum=#{forum}, 	   url=#{url},forum_Type=#{forumType},COMMUNITY_UUID=#{communityUuid} 	   where maintain_id=#{id} 	  	              delete from TLD.Admin_Maintain where maintain_id = #{id}            

清单 10. 数据库表定义语句
 CREATE TABLE "TLD"."ADMIN_MAINTAIN"  (  "MAINTAIN_ID" BIGINT NOT NULL GENERATED ALWAYS AS IDENTITY (      START WITH +0      INCREMENT BY +1      MINVALUE +0      MAXVALUE +9223372036854775807      NO CYCLE      CACHE 20      NO ORDER ) ,   "AGENTTEAM" CHAR(50) NOT NULL ,   "FORUM" CHAR(50) NOT NULL ,   "URL" CHAR(200) NOT NULL ,   "FORUM_TYPE" INTEGER NOT NULL ,   "COMMUNITY_UUID" CHAR(50) NOT NULL )   		 IN "USERSPACE1" ;

下面对上面的 dao 代码进行单元测试:

清单 11.DAO 单元测试代码
 public class TestAdminMaintainDao { 	 private Logger logger = Logger.getLogger(this.getClass()); 	 private ClassPathXmlApplicationContext ctx = \	 new ClassPathXmlApplicationContext("applicationContext.xml"); 	 // 从配置文件中初始化实例     private AdminMaintainDao dao = \     (AdminMaintainDao)ctx.getBean("adminMaintainDao");   /** 测试查询 */  @Test  public void testGetAllAdminMaintain() { 					  List list=dao.getAllAdminMaintain(); 	  for (AdminMaintain am : list) { 			            logger.info(am.getAgentTeam()+" : "+am.getUrl()); 	      //add your code here; 	  } 		  }  /** 测试新增 */  @Test  public void testAdd() { 				  AdminMaintain am=new AdminMaintain(); 	  am.setAgentTeam("C"); 	  am.setCommunityUuid("UUidssss8080-980sf"); 	  am.setForum("C Awards"); 	  am.setUrl("http:// c wards"); 	  am.setForumType(1); 	  dao.add(am); 	    } 	 /** 测试修改 */  @Test  public void testUpdate() { 			  AdminMaintain am=new AdminMaintain(); 	  am.setId(11); 	  am.setAgentTeam("D"); 	  am.setCommunityUuid("sfs080-sfs");       am.setForum("D FAQ"); 	  am.setUrl("http:// D FAQ"); 	  am.setForumType(3); 	  dao.update(am); 	    } 	 /** 测试删除 */   @Test   public void testDelete() { 		  dao.delete("11");   }  }

测试:测试示例应用程序

登录到编辑页面,EnhancedGrid 就会从后台把记录列出来,点击“新增”按钮,就会新增一条空的记录。编辑该记录,编辑完后数据就自动的保存到后台数据库,选中所要删除的记录 ( 可以多选 ),点击删除按钮,确认删除后会把所选的记录全部删除,整个过程不用刷新页面就能完成,感觉就像在操作 Excel 表格一样 ( 如图 1),用户交互性很强。

结束语

EnhancedGrid 是一个功能强大、丰富的表格控件,提供伸缩式的表格数据渲染,还包括分页、排序、过滤、列移动、选择、编辑、键盘导航,导出和打印功能等等。 EnhancedGrid 与 store 的紧密联系使得在 Grid 中对数据处理进行配置非常容易,通过与 dojo.connect 在一起使用可以很容易的来绑定自定义函数来实现对表格数据的异步存储。

 require(['dojo/_base/array', 'dojo/_base/lang', 'dojo/_base/event',  'dojo/on', 'dojox/grid/EnhancedGrid', 'dojo/data/ItemFileWriteStore',  'dijit/form/Button', 'dojo/dom', 'dojo/parser', 'dojo/domReady!'],   function(array, lang, event, on, EnhancedGrid, ItemFileWriteStore,           Button, dom, parser){  parser.parse();}

 // 定义 layout 布局,就是定义标题的列标题,宽度 var layout = [[       {'name': 'Id', 'field': 'id', 'width': '3%', editable: true},       {'name': 'Agent Team', 'field': 'agentTeam', 'width': '8%', editable: true},       {'name': 'Community UUid', 'field': 'communityUuid', 'width': '17%', \       editable: true},       {'name': 'View members', 'field': 'communityUuid', 'width': '10%',\       formatter: formatHref},      {'name': 'Forum name', 'field': 'forum', 'width': '10%', editable: true},       {'name': 'Forum Type', 'field': 'forumType', 'width': '6%', editable: true,\       type: dojox.grid.cells.Select, options:["Awards","Program","FAQ","HelpDesk",\       " Best Practices"], values: [ '1', '2','3','4','5' ],formatter: formatForumType},  	  {'name': 'URL', 'field': 'url', 'width': 'auto', editable: true}  ]];

 grid = new EnhancedGrid({         id: 'grid',         store: store,// 连接 datastore 控件        structure: layout,         rowSelector: '20px'});    /* 把 grid 控件加入到 HTML div 标签中 */    grid.placeAt("gridDiv");  );

 function onNew(item) {    // 得到表格单元的内容     var agentTeam=item.agentTeam; 	 var communityUuid=item.communityUuid;      var forum=item.forum; 	 var url=item.url; 	 var forumType=item.forumType; 		 dojo.xhrPost({        //The URL of the request        url: "/yourproject/new",        // 要处理内容的格式       handleAs: "json",        // 请求参数      content: { 				        agentTeam: agentTeam,         communityUuid: communityUuid, 	        forum: forum,         url: url,         forumType: forumType        },           // 错误处理函数      error: function(error, ioArgs) {          alert(error.message); 		    } 	   }); 	 // 添加记录后自动刷新页面 ;  setTimeout(function() {window.location.reload();},2000);  }

           Add One Row                   Remove Selected Rows     

 grid = new EnhancedGrid({         id: 'grid',         store: store,// 连接 datastore 控件        structure: layout,         rowSelector: '20px'});/    /* 把 grid 控件加入到 HTML div 标签中 */    grid.placeAt("gridDiv");  // 以下为绑定函数到特定的事件 // 绑定函数到新增功能	 dojo.connect(store, "onNew", this,onNew);  // 绑定函数到修改功能	 dojo.connect(store, "onSet", this,onSet);  // 绑定函数到删除功能	 dojo.connect(store, "onDelete", this,onDelete);     /* 为 button2 按钮添加 click 处理事件 */     on(button2,'click',        function(e){         /* set the properties for the new item: */         var myNewItem = {id: '', agentTeam: 'new',communityUuid:'new',forum: 'new', \        url: 'new', forumType: 0};         /* Insert the new item into the store:*/         store.newItem(myNewItem);// 新增行    }     );  /* 为 button1 按钮添加 click 处理事件 */    on(button1,'click',     function(e){  /* 选择被选中的行 */         var items = grid.selection.getSelected(); 		        if(items.length){            if(!confirm("Are you sure to delete?")){               return false; 					 }             /* 循环遍历所选择的行 */             array.forEach(items, function(selectedItem){                 if(selectedItem !== null) {                     /* Delete the item from the data store: */                     store.deleteItem(selectedItem);                 } /* end if */             }); /* end forEach */         }else{            alert("Please select row(s)");  }         event.stop(e);     }  );

                                                         

 public interface AdminMaintainDao { 	 /** 从数据库中查询所有的记录 *  */ 	 public List getAllAdminMaintain(); 	 /** 新增记录 *  */ 	 public void add(AdminMaintain am);  /** 修改记录 *  */ 	 public void update(AdminMaintain am);  /** 删除记录 *  */ 	 public void delete(String id); 	 }

      	               	      	      	      	         	  	    	       	                                    insert into TLD.Admin_Maintain(agentTeam, forum, url,forum_Type,COMMUNITY_UUID)	   values (#{agentTeam}, #{forum}, #{url},#{forumType},#{communityUuid}) 	  	     	   update TLD.Admin_Maintain set agentTeam=#{agentTeam}, forum=#{forum}, 	   url=#{url},forum_Type=#{forumType},COMMUNITY_UUID=#{communityUuid} 	   where maintain_id=#{id} 	  	              delete from TLD.Admin_Maintain where maintain_id = #{id}            

 CREATE TABLE "TLD"."ADMIN_MAINTAIN"  (  "MAINTAIN_ID" BIGINT NOT NULL GENERATED ALWAYS AS IDENTITY (      START WITH +0      INCREMENT BY +1      MINVALUE +0      MAXVALUE +9223372036854775807      NO CYCLE      CACHE 20      NO ORDER ) ,   "AGENTTEAM" CHAR(50) NOT NULL ,   "FORUM" CHAR(50) NOT NULL ,   "URL" CHAR(200) NOT NULL ,   "FORUM_TYPE" INTEGER NOT NULL ,   "COMMUNITY_UUID" CHAR(50) NOT NULL )   		 IN "USERSPACE1" ;

下面对上面的 dao 代码进行单元测试:

 public class TestAdminMaintainDao { 	 private Logger logger = Logger.getLogger(this.getClass()); 	 private ClassPathXmlApplicationContext ctx = \	 new ClassPathXmlApplicationContext("applicationContext.xml"); 	 // 从配置文件中初始化实例     private AdminMaintainDao dao = \     (AdminMaintainDao)ctx.getBean("adminMaintainDao");   /** 测试查询 */  @Test  public void testGetAllAdminMaintain() { 					  List list=dao.getAllAdminMaintain(); 	  for (AdminMaintain am : list) { 			            logger.info(am.getAgentTeam()+" : "+am.getUrl()); 	      //add your code here; 	  } 		  }  /** 测试新增 */  @Test  public void testAdd() { 				  AdminMaintain am=new AdminMaintain(); 	  am.setAgentTeam("C"); 	  am.setCommunityUuid("UUidssss8080-980sf"); 	  am.setForum("C Awards"); 	  am.setUrl("http:// c wards"); 	  am.setForumType(1); 	  dao.add(am); 	    } 	 /** 测试修改 */  @Test  public void testUpdate() { 			  AdminMaintain am=new AdminMaintain(); 	  am.setId(11); 	  am.setAgentTeam("D"); 	  am.setCommunityUuid("sfs080-sfs");       am.setForum("D FAQ"); 	  am.setUrl("http:// D FAQ"); 	  am.setForumType(3); 	  dao.update(am); 	    } 	 /** 测试删除 */   @Test   public void testDelete() { 		  dao.delete("11");   }  }

Leave a Comment

Your email address will not be published.