- 浏览: 57192 次
- 性别:
- 来自: 河北
最新评论
随着 web 的广泛应用,web application 的开发项目越来越多,而大部分的 web 开发归根结底都是对数据库的增删改查。对于一张数据库表的增删改查,采用基于 MVC(模型 - 视图 - 控制器)设计模式的开发往往需要 Domain class、 Controller、4 个增删改查的页面、form 输入数据的校验等。这样就免不了大量类似功能的复制粘贴修改。 如果你想从重复劳动中解放出来,Grails 是一个很好的选择。 使用 Grails 只需要写一个 domain class 就可以自动生成 CRUD 4 个页面和对应的 controller 方法来实现对数据库的增删改查,并实现服务器端的数据校验,从而大大地提高了开发效率。
类似的 web 快速开发框架还有 Rails、Django 和 TurboGears 等,与它们相比 Grails 有如下优势:
- Grails 是一套用于快速 Web 应用开发的开源框架,它是由 15% 的 Groovy 代码和 85% 的 Java 代码写成,并建立在千锤百炼的经典框架(Spring,Hibernate,Sitemesh)之上,从而为大家带来一套能实现超高生产力的一站式框架。
- Grails 学习快速,使用容易,如果你熟悉 Hibernate、Spring、SiteMesh 和 JSP,那么你一个小时之内就能学会 Grails。
- Grails 运行于 JVM 之上,生成的 war 包可以部署在各种成熟的服务器 Tomcat, JBoss,Weblogic,Webspere 等。
- 性能与 Rails2(100% Ruby) 相比要快很多。
- Grails 被 SpringSource 收购,有强大的技术支持。
采用 Grails 的默认模板生成的 CRUD 页面有些简陋,实际项目中往往需要功能更加丰富,外观更加漂亮,使用更加方便的控件。例如页面布局控件,Tab 控件,树,菜单,日历,编辑器,进度条等等。现在有很多 JS 框架致力于 web UI, 例如 Dojo, Extjs, Jquery, YUI 等。我们之所以选择 Dojo, 因为 Dojo 具有以下的优势:
- 组件丰富,有强大的 UI(Dijit)。这样一来就可以减轻我们的大量沉重的工作,而且目前的开发非常活跃。
- UI 外的功能也很强大,Full Stack 的框架,扩展了 DHTML 的能力,例如:
- 支持与浏览器 Back/Forward 按钮的集成。
- Dojo Offline,一个跨平台的离线存储 API。
- Chart 组件,可以方便地在浏览器端生成图表。
- 基于 SVG/VML 的矢量图形库。
- Google Maps、Yahoo! Maps 组件,方便开发 Mashup 应用。
- Dojox Socket, 基于 WebSocket 或者 XHR 长连接的“服务器推”技术。
- Dojo 是一个很好的基础架构,具有面向对象的设计,统一的命名空间,包管理机制(The Package System and Custom Builds)可扩展性。Dojo 其实是一个组件模型,类似于 Spring,用来支持大规模的组件化开发。组件模型的作用就是增强代码的重用,这对于提高开发效率是至关重要的。
- Dojo 的背后有强大的支持 IBM、Oracle 等,这是非常重要的优势。Dojo 现在已经是众多开源框架的选择,包括:WebWork、Tapestry、Eclipse ATF、MyFaces。Dojo 的开发团队由 Alex Russell 领军,人数众多,力量非常强大。
- 开源,使用 BSD 软件许可。
首先需要安装 JDK,并设置好 JAVA_HOME。本文使用的是 JDK 1.6。
- Grails 官方网站下载并解压 grails.zip。本文使用的是 Grails1.3.7。
- 创建一个 GRAILS_HOME 环境变量,将 $GRAILS_HOME/bin 添加到 PATH 中。
- 习惯用 IDE 的可以选择 Intellij idea,Netbeans 和 Spring STS。本文以免费的 Netbeans7.0.1 为例,配置很简单,只需要在 Tools->Options->Miscellaneous->Groovy 下设置 Grails Home,就可以进行 Grails 项目的开发了。(见图 1)
图 1. Netbeans 中 Grails 的配置
- 首先,在一个空白目录下,输入 grails create-app DojoGrails。稍后,可以看到一个名为 DojoGrails 的目录 , 会建立一个标准的目录结构(如图 2)。很明显这是层次清晰的 MVC 模式。
图 2. Grails 的目录结
- 进入 DojoGrails 目录,并输入 grails create-domain-class com.shuo.Employee,将得到两个新的文件:域类 grails-app\domain\com\shuo\Employee.groovy 和一个单元测试类。这里我们主要关注域类。一开始,域类里面什么都没有,我们为它添加一些字段和约束,见清单 1:
清单 1. 域类 Employeeclass Employee { String employeeNumber String name Integer age Date onboardDate static constraints = { employeeNumber blank: false, unique: true name blank: false } String toString(){ return "${name}(${employeeNumber})" } }
类似地我们创建一个与 Employee 关联的域类 Product。Product 的负责人 owner 是 Employee。见清单 2:
清单 2. 域类 Productclass Product { String name String description Float price Employee owner static constraints = { } }
这两个域类覆盖了所有常见的数据库数据类型(Date,String,Integer,Float)。
- 在命令行输入 grails create-controller com.shuo.Employee 生成域类对应的 Controller, 使用 grails 强大的脚手架 scaffold,见清单 3。运行时在内存中将动态地生成所有 CRUD 的 method 以及 CRUD 的页面。同样地生成 ProductController。
清单 3. 控制器代码class EmployeeController { def scaffold = true } class ProductController { def scaffold = true }
- 到这里对两张表的 CRUD 功能全部完成,是时候看看效果了,在命令行输入 grails run-app。通过浏览器 http://localhost:8080/DojoGrails, 可以看到如图 3 所示的 Employee 的 List 页面。
图 3. Scaffold 默认的 List 页面
可以看出这个 list 页面已经具备分页和排序功能了。
再来看看 Employee 的 Create 页面,如图 4 所示:
图 4. Scaffold 默认的 create 页面
从图中可以看出添加后,服务器端会返回数据验证的结果,比如员工号必须唯一,名字不能为空,年龄必须是数字等。
再来看看 Product 的 Edit 页面 , 如图 5:
图 5. Product 的 Edit 页面
这里我们主要关注 owner 字段 , scaffold 自动生成一个列出所有员工的下拉框,让用户从中选择出产品的负责人。
另外值得一提的是,这个 web application 已经拥有了国际化多语言的支持,只需要对 grails-app/i18n/messages_zh_CN.properties 文件进行修改,就可以汉化整个 web 界面了。
Grails 的 Scaffold 脚手架是不是很强大呢,一句 def scaffold = true 就实现了如此丰富的功能。
-
在命令行输入 grails generate-all com.shuo.Employee,grails 会根据默认模板在 EmployeeController 中生成 CRUD 的对应的 method 代码,并在 grails-app/views/employee 下生成 4 个 CRUD 的 gsp 页面。这些就是 scaffold 背后的代码。后面我们将对这些代码进行修改,引入 Dojo,来增强 CRUD 的用户体验。
到 dojotoolkit.org 网站下载最新版本的 Dojo Toolkit Release。 本文用的是 dojo-release-1.6.1。
解压后将其拷入 web-app/js 中,目录结构如图 6:
图 6. Dojo 的目录结构
在 grails-app/views/layouts/main.gsp 中添加以下代码,见清单 4:
清单 4. 导入 Dojo
<link rel="stylesheet" href="${resource(dir:'js/dojo-release-1.6.1/dijit/themes/claro',file:'claro.css')}" /> <script src="${resource(dir:'js/dojo-release-1.6.1/dojo',file:'dojo.js')}" data-dojo-config="isDebug: true,parseOnLoad: true,locale:'zh'"></script> … <body class="claro"> |
Grails scaffold 默认的 List 表格(如图 3)功能较为简单,例如它不能像 excel 那样的跨行列合并单元格、冻结表头、单击单元格转成编辑状态等。而 Dojo 为我们提供了这样的控件:Dojox 的 DataGrid 像一个基于 Web 的 Excel 组件,足可以应付非常复杂的数据展示及数据操作。下面列出了 DataGrid 的一些特性:
- 可以任意的增加和删除单元格、行、或者列;
- 对行进行统计摘要,Grid 可以生成类似于 OLAP 分析的报表;
- Grid 超越了二维表格的功能,它可以跨行或跨列合并单元格以满足不同的数据填充的需求;
- 行列冻结功能,使得浏览数据更加灵活方便;
- Grid 事件采用了钩子机制,我们可以通过 onStyle 钩子完成对样式的更改;
- 单元格具备富操作,所有的 dijit 部件都可以在单元格中使用,并且单元格可以通过单击转换为编辑状态;
- 可以为不同的单元格设置不同的上下文菜单;
- Grid 嵌套,也就是说 Grid 可以在单元格中嵌套其他的 Grid,从而组成更为复杂的应用
- 支持 Dnd 拖放和键盘 navigation,提高了 Accessibility。
- 除此之外,Grid 还有具有其他很多特性,例如,非常实用的偶数行上色、灵活的选取功能、自动调整列宽、数据的展开/合闭等。
其中 DataGrid 有一个非常重要的特性就是虚拟滚动(Virtual Scroll),DataGrid 对付大数据源的时候,在滚动事件触发后才请求后面的数据并创建 DOM 结点,因此每次只需要显示很少的几行,从而加快了 Grid 的加载。 不得不承认这是一种很精巧的做法,在用户体验上也很自然,就好像所有数据本来就在那里一样。但它也有缺点,当数据源真的比较大的时候,滚动条就会非常小,再加上行高参差不齐的情况,要精确地滚动到某个位置就比较困难。这时候简单直观的分页机制就足够了。最新的 Dojo 1.6 版里,继承自 DataGrid 的 EnhancedGrid 引入了几个新的插件,其中就有 Pagination(分页浏览)插件。
首先在 list.gsp 中使用 EnhancedGrid, 见清单 5:
清单 5. Dojo 的 EnhancedGrid 代码
<script type="text/javascript"> dojo.require("dojox.grid.EnhancedGrid"); dojo.require("dojox.grid.enhanced.plugins.Pagination"); dojo.require("dojox.data.QueryReadStore"); var myStore = new dojox.data.QueryReadStore({url:"listJson"}); function onRowDblClick(e){ var itemid = grid.getItem(e.rowIndex).i.id; document.location.href="edit/"+itemid; } </script> <style type="text/css"> @import "${resource(dir:'js/dojo-release-1.6.1/dojox/grid/enhanced/resources/claro', file:'EnhancedGrid.css')}"; </style> … <table dojoType="dojox.grid.EnhancedGrid" jsId="grid" store="myStore" rowsPerPage="5" clientSort="true" style="width: 100%; height: 280px;" onRowDblClick= "onRowDblClick" rowSelector="20px" plugins="{ pagination: { pageSizes:['5','10','20'], maxPageStep: 5, descTemplate: '${message(code: 'default.paginateDescTemplate')}', description: true, sizeSwitch: true, pageStepper: true , gotoButton: true }}"> <thead> <tr> <th width="50px" field="id" >ID</th> <th width="100px" field="employeeNumber"> 员工号 </th> <th width="100px" field="name"> 姓名 </th> <th width="100px" field="age"> 年龄 </th> <th width="100px" field="onboardDate"> 入职日期 </th> </tr> </thead> </table> |
在 employeeController 中加入方法,见清单 6:
清单 6. EnhancedGrid 对应的控制器代码
def listJson = { if(params.start?.isInteger()){ params.put("offset",params.int('start')) } params.put("max",params.count?params.int('count'):5) if(params.sort?.startsWith("-")){ params.put("sort",params.sort.substring(1)) params.put("order","desc") } def total = Employee.count(); def results = Employee.list(params) def jsonData = [identifier:"id",numRows: total,items: results] render jsonData as JSON } |
这样就完成了 EnhancedGrid 和服务器,数据库的交互。以上代码实现了分页、排序、双击表中一行进入修改界面,效果如图 7:
图 7. EnhancedGrid 界面
以上代码有两点需要注意:
- 从图 3 可以看出默认的日期格式是 yyyy-MM-dd HH:mm:ss z,这是由 messages.properties 中的 default.date.format 定义的。而对于 JSON 中的日期数据,格式化需要在 config.groovy 中加入如清单 7 的代码:
清单 7. JSON 的日期格式化代码import grails.converters.JSON; class BootStrap { def init = { servletContext -> JSON.registerObjectMarshaller(Date) { return it?.format("yyyy-MM-dd") } } ...
- 这段代码有个小技巧。因为 ${} 在 gsp 文件中是保留字,如果把 pagination 插件的 descTemplate 属性值“
${2} - ${3} 共 ${1}${0}
” 直接写在 gsp 页面上会出错,所以写到 messages 文件里。default.paginateDescTemplate=${2} - ${3} 共 ${1}${0}
Grails scaffold 为外键关联的对象做了下拉框供用户选择,而这个下拉框是个简单的 HTML select 控件(如图 5),用户只能从一堆下拉选项中肉眼找出要选的关联对象。本文例子中,用户就需要从一大堆的企业员工中肉眼找出要选的产品负责人。企业员工常常是成百上千人,要从 select 下拉框中找出一个很难,而且一次性从数据库读取所有员工的姓名到下拉框也很耗时。Dojo 为我们提供了一个类似于 HTML 的 select 控件:FilteringSelect,但它可以动态输入,并且按照输入值列出匹配的可选项,甚至可以按需设置加载选项的数量,下拉列表的选项可以从数据库动态获取。这样用户就能通过输入关键字,让程序帮我们找出要选的对象。
Dojo 的 ComboBox 跟 FilteringSelect 非常类似,不过 FilteringSelect 不允许用户输入可选项之外的值,而 ComboBox 可以输入任意值。所以这里我们选择 FilteringSelect。
首先对 create.gsp 和 edit.gsp 做修改,如清单 8:
清单 8. Dojo 的 FilteringSelect 代码
<script type="text/javascript"> dojo.require("dijit.form.FilteringSelect"); dojo.require("dojox.data.QueryReadStore"); var employeeSelector= new dojox.data.QueryReadStore({ url:"${resource(dir:'employee')}/combo"}); </script> … <input name="owner.id" dojoType="dijit.form.FilteringSelect" store="employeeSelector" placeHolder="请选择员工" pageSize="5" autocomplete="false" value="${productInstance?.owner?.id}" queryExpr="${message(code: 'default.queryExpr')}"></input> |
再为 employeeController 增加方法,从数据库获取下拉选项,如清单 9:
清单 9. FilteringSelect 对应的控制器代码
def combo ={ def criteria='',results if(params.id){ results = Employee.get(params.id) } if(params.name){ criteria = params.name.replace('*','%') results = Employee.findAllByNameLikeOrEmployeeNumberLike( criteria,criteria, [max:params.count?(params.count+1):1,offset:params.start]) } render(contentType: "text/json") { identifier = "id" label = "name" items = array{ results.each {w -> item("id":w.id,"name":w.name) } } } } |
以上代码实现了按照员工姓名模糊查找选择员工(如图 8 左上),按照员工号模糊查找选择员工(如图 8 中),查找结果分页(如图 8 右),如果输入框的值不在员工中,有出错提示(如图 8 左下)。
图 8. FilteringSelect 界面
Grails scaffold 提供下拉框让用户选择年月日(如图 4),从下拉框中选较为麻烦。常常我们喜欢直接输入日期或者是查看日历找到想填的日期。如图 9 的日期选择控件就满足了我们的需要。这个界面漂亮操作简单的日期选择控件就是 Dojo 的 DatePicker,我们可以通过在页面加入以下代码来实现,见清单 10:
图 9. DatePicker 界面
清单 10. Dojo 的 DateTextBox 代码
<input type="text" name="onboardDate" dojoType="dijit.form.DateTextBox" required="true" value="<g:formatDate date='${employeeInstance?.onboardDate}'/>" /> |
因为默认的 scaffold 时间控件的年月日是分 3 个字段分别上传,和我们用一个字段上传的数据结构是不同的,所以 controller 中需要对其做特殊转换,见清单 11:
清单 11. 使用 DateTextBox 后,save 方法添加的日期转换代码
def save = { if(params.onboardDate!=null&&!params.onboardDate.isEmpty()){ try{ params.onboardDate =Date.parse("yyyy-MM-dd",params.onboardDate) }catch(Exception e){ employeeInstance.errors.rejectValue("onboardDate", "typeMismatch.java.util.Date", [message(code: 'employee.label', default: 'Employee')] as Object[], "日期格式不对") render(view: "create", model: [employeeInstance: employeeInstance]) } } def employeeInstance = new Employee(params) ... } |
Grails 的脚手架已经具备了服务器端数据校验的功能,但是每次都将数据提交到服务器再进行校验,性能太低了。为减轻服务器的负载,在浏览器端做 Javascript 校验,就非常必要。Dojo 正好提供了一个很好的数据检验框架。
用户输入数据的同时 Dojo 就会进行数据检验,一旦出错立即高亮显示出错信息。
例如用 dijit.form.NumberTextBox 可以进行数字的校验,下面的代码要求价格是大于等于 0 的数字,见清单 12:
清单 12. NumberTextBox 的数据验证
<g:textField name="price" value="${fieldValue(bean: productInstance, field: 'price')}" dojoType="dijit.form.NumberTextBox" constraints="{min:0}" required="true" invalidMessage="大于 0 的数字 ." /> |
另外,当表单提交时可以判断表单是否有效,如果包含无效数据将不提交表单。见清单 13:
清单 13. 表单的数据验证
function validateData(form1){ if(!form1.validate()) { alert('请先纠正无效数据,然后再提交!'); return false; } return true; } … <g:form method="post" dojoType="dijit.form.Form" onsubmit="return validateData(this);" > |
以上代码的效果类似于图 8(左下)中的红边框及出错提示。
从前面的文章我们已经能窥见 Dojo 结合 Grails 开发 web application 的强大能力以及效率。此外 Dojo 还有很多优秀的布局控件,对话框,图表,菜单,多文件上传控件等等,能够实现非常复杂的企业级应用。即将发布的 Grails2.0 也添加了很多很好的特性。我们有理由相信 Dojo 结合 Grails 的前途非常广阔。如果你们觉得文中的代码有更好的实现方法,欢迎与我联系(zhyjzhan@cn.ibm.com)。
相关推荐
这个项目实现了基于这两者的增删改查功能,适用于基本的数据管理需求。 首先,我们来详细了解一下Struts框架。Struts提供了一种结构化的、可扩展的方式来组织和控制应用程序的行为。它将业务逻辑、表示层和控制层...
**增删改查操作实现:** 1. **添加(Create)**:使用Session的save()或saveOrUpdate()方法将新对象存入数据库。 2. **删除(Delete)**:通过Session的delete()方法删除指定对象,对应的数据库记录会被移除。 3. **...
在这个"struts2增删改查"的主题中,我们将深入探讨Struts2如何支持基本的数据操作,包括添加(Add)、删除(Delete)、修改(Update)和查询(Query)。 1. **Struts2架构基础**: Struts2的核心是Action类,它是...
在给定的“dojo 在线操作表格”主题中,Dojo的Grid组件是核心知识点,它提供了强大的数据展示和管理功能,支持在线的增删改查操作。 Dojo Grid允许用户在网页上直接操作表格数据,提供了丰富的功能,包括但不限于:...
在这个"Struts2 Spring Jpa Ajax Dojo crud小例子"中,我们将深入探讨这些技术如何协同工作以实现数据的增删改查(CRUD)操作。 **Struts2** 是一个基于MVC(Model-View-Controller)设计模式的Java Web框架,用于...
- **熟悉Grails与Dojo的集成**:包括使用Dojo的标签、库和抽象层进行开发。 - **深入控制器与动作**:学习如何使用控制器处理Ajax请求,掌握`render()`方法、构建器和模板等关键概念。 - **获取实用技巧和资源**:...
本篇文章将深入探讨如何利用dojo enhancedGrid实现分页功能,以及如何通过静态数据和JSON文件加载数据,并使用XHR(XMLHttpRequest)进行数据请求。同时,我们还将讨论如何使用fetch API实现分页、过滤和排序。 ...
DOjo提供了三种安装方式:使用美国在线(AOL)的内容分发网络(CDN),在本地使用稳定版本,和从DOjo网站的SVN服务器上获取最新代码。使用CDN上的DOjo非常快捷,用户只需添加一个script标签到HTML文件中就可以使用...
4. **dojo/store**:这是一个数据存储抽象层,提供了一种统一的方式来访问和操作数据,无论数据源是JSON、XML还是其他格式。 5. **dojo/Widget**:Dojo提供了丰富的UI组件,如按钮、表单、对话框等,它们都继承自`...
总结,"AJAX之Dojo实现登陆框"涵盖了使用Dojo库进行AJAX请求,构建无刷新登录表单,处理表单数据,以及相关的事件监听和响应处理。在实践中,还需要结合前端验证、后端安全措施以及用户体验设计来创建一个完整的登录...
Dojo 提供了数据存储抽象层,如`dojo/store`和`dojo/data`,允许开发者与各种数据源(如JSON、XML或REST API)交互,提供了数据查询、增删改查等操作。 5. **dijit** 组件库 `dijit`是Dojo的UI组件库,包括各种可...
上述示例展示了如何使用 `dojo.xhrGet` 发起一个 JSON 格式的 GET 请求,并处理响应数据。 #### 总结 通过以上介绍,我们可以看到 Dojo 不仅提供了丰富的功能,还保持了较高的灵活性和易用性。无论是基本的 DOM ...
### Dojo控件的使用和入门心得 #### Dojo简介及其优势 Dojo是一个功能强大的JavaScript框架,专门设计用于简化富互联网应用(RIA)的开发。作为一种DHTML Toolkit,Dojo封装了大量的常用功能,旨在提高前端开发...
本教程将深入探讨Dojo框架的使用,帮助开发者更好地理解和应用这一工具。 首先,从`dojo-release-0.9.0.tar.gz`这个文件名可以看出,这是一个Dojo框架的早期版本,版本号为0.9.0。在学习过程中,了解不同版本间的...
通过使用dojo.require和dojo.provide,开发者可以实现代码的模块化,提高代码复用性和可维护性。 Dojo的dojo.query方法类似于jQuery的选择器,用于选取页面中的DOM元素。此外,Dojo还提供了一套强大的DOM操作API,...
2. **DOM操作**:Dojo提供了一套完整的DOM操作API,方便开发者对HTML元素进行增删改查。 3. **数据绑定**:Dojo的数据绑定机制使得视图和模型可以保持同步,降低了数据驱动UI的复杂性。 4. **Widget系统**:Dojo...
这些例子可以帮助开发者更好地理解和学习如何使用Dojo来处理各种Web开发任务,尤其是涉及到数据展示和交互的报表功能。 1. **Dojo的核心特性**:Dojo的核心特性包括模块系统(AMD,Asynchronous Module Definition...
本例主要介绍了如何使用Dojo来创建一个具有增删改查功能的动态树。 首先,让我们了解Dojo Tree的基本结构。Dojo Tree由`dijit/tree/ForestStoreModel`作为数据模型,`dijit/Tree`作为视图层,以及`dojo/store`作为...