首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

精通 Grails 用 Groovy 服务器页面(GSP)改变视图(1)

精通 Grails 用 Groovy 服务器页面(GSP)改变视图(1)

查看 Grails 应用程序Grails 使用 GSP 作为表示层。Groovy 服务器页面中的 Groovy 不仅代表底层技术,还代表可以快速编写一两个 scriptlet 的语言。从这方面来说,GSP 非常类似于 Java™ 服务器页面(JSP)技术,JSP 允许在 Web 页面上混合使用一些 Java 代码,也和 RHTML(Ruby on Rails 的核心视图技术)非常相像,RHTML 允许在 HTML 标记之间插入一些 Ruby 代码。
当然,Java 社区长期以来都不欣赏小脚本。scriptlet 会导致最低形式的技术重用 —复制与粘贴 — 以及其他一些在技术方面为人所不齿的恶行(因为你能因为你应该 之间有巨大区别)。GSP 中的 G 对优秀、正直的 Java 人员来说只应该表示一种实现语言而不是其他。Groovy TagLibs 和片段模板提供了在 Web 页面之间共享代码和行为的一种更成熟的方式。
GSP 是 Grails 以页面为中心的 MVC 观点的基础。页面是基本衡量单位。列表页面提供了到 Show 页面的链接。Show 页面支持单击到编辑页面,诸如此类。不论是熟练的 Struts 开发人员还是最近的 Rails 爱好者,都熟悉这种 Web 生命周期。
之所以提到这点,是因为近几年出现了大量不以页面为中心的视图技术(请参阅 )。面向组件的 Web 框架(例如 JavaServer Faces (JSF) 和 Tapestry 越来越受到青睐。Ajax 革命派生出大量基于 JavaScript 的解决方案,例如 Dojo 和 Yahoo! UI (YUI) 库。富 Internet 应用程序(RIA)平台,例如 Adobe Flash 和 Google Web Toolkit (GWT) 承诺能够实现方便的 Web 部署,并提供更加丰富、与桌面类似的用户体验。幸运的是,Grails 能够轻松地处理所有这些视图技术。
MVC 关注点隔离的整体要点在于:它能够使您轻松地用自己喜欢的任何视图作为 Web 应用程序的外观。Grails 流行的插件基础设施意味着许多 GSP 替代物不过是 grails 安装的插件(请参阅  获得可用插件的完整列表的链接,或者在命令行下输入 grails list-plugins)。许多插件都是由社区驱动的,是那些希望将 Grail 与他们喜欢的表示层技术一起使用的人们的努力结果。
虽然 Grails 没有内置 JSF 的自动挂勾(hook),但是仍然可以结合使用这两种技术。Grails 应用程序是标准的 Java EE 应用程序,因此可以将相应的 JAR 放在 lib 目录内,将需要的设置放在 WEB-INF/web.xml 配置文件内,并像平常一样编写应用程序。Grails 应用程序部署在标准的 servlet 容器内,所以 Grails 对 JSP 的支持同对 GSP 的支持一样好。Grails 有针对 Echo2 和 Wicket 的插件(两者都是面向组件的 Web 框架),所以在使用 JSF 或 Tapestry 插件方面没有任何障碍。
类似地,向 Grails 添加 Ajax 框架(例如 Dojo 和 YUI)的步骤也没有什么特别之处:只要将它们的 JavaScript 库复制到 web-app/js 目录即可。Prototype 和 Scriptaculous 是 Grails 的默认安装。RichUI 插件则从各种 Ajax 库选择 UI 部件。
如果查看插件列表,那么就会看到对 RIA 客户机的支持 —— 例如 Flex、OpenLazlo、GWT 和 ZK。显然,Grails 应用程序并不缺少备选的视图解决方案。但是在这里我们还是采用 Grail 直接支持的视图技术 — GSP。
GSP 101可以使用多种方法查找 GSP 页面。文件扩展名 .gsp 就是一种很明显的方法,就好像很多以 <g: 开头的标记一样。事实上,GSP 不过是标准 HTML 加上一些提供动态内容的 Grails 标记而已。在前一节提到的某些备选的视图技术是一层不透明的抽象层,完全将 HTML、CSS 和 JavaScript 的细节隐藏在 Java、ActionScript 或其他编程语言层之后。GSP 是在标准 HTML 上的薄薄的一层 Groovy 层,因此在必要时,可以轻松地将它从框架中取出来,并使用原生的 Web 技术。
但是要在目前的 Trip Planner 应用程序中查找 GSP 的话,则会比较费力(这个系列的前两篇文章开始构建 Trip Planner 程序。如果没有跟上进度,那么现在是赶上来的好时机)。现在视图正在使用动态搭建(scaffold),所以 trip-planner/grails-app/views 目录还是空的。请在文本编辑器打开如清单 1 所示的 grails-app/controller/TripController.groovy,查看用来启用动态搭建的命令:
清单 1. TripController 类
1
2
3
class TripController{
  def scaffold = Trip
}




def scaffold = Trip 行告诉 Grails 在运行的时候动态地生成 GSP。这非常适合自动保持视图与域模型修改同步,但是如果正在学习该框架,那么它没有提供太多可学习的东西。
请在 trip-planner 根目录下输入 grails generate-all Trip。当询问是否覆盖现有控制器时,回答 y(也可以回答 a 表示全部,这将覆盖所有内容,这样就不会反复出现提示)。现在应该看到完整的 TripController 类,带有名为        create、edit、list 和 show 闭包(以及其他闭包)。还应该看到 grails-app/views/trip 目录下有四个 GSP:create.gsp, edit.gsp, list.gsp, and show.gsp.
在这里起作用的是 “约定优于配置”。当访问 http://localhost:9090/trip-planner/trip/list 时,就是要求 TripController 填充域模型对象        Trip 的列表,并将列表传递给        trip/list.gsp 视图。请在文本编辑器中查看 TripController.groovy,如清单 2 所示:
清单 2. 完全填充的 TripController 类
1
2
3
4
5
6
7
8
class TripController{
  ...
  def list = {
    if(!params.max) params.max = 10
    [ tripList: Trip.list( params ) ]
  }
  ...
}




这个简单的闭包从数据库中检索到 10 条 Trip 记录,将它们转换为 POGO,并将它们保存在名为 tripList 的 ArrayList 内。list.gsp 页面随后将遍历列表,逐行构建 HTML 表格。
下一节研究许多流行的 Grails 标记,包括用来在 Web 页面上显示每条 Trip 的        <g:each> 标记。
Grails 标记<g:each> 是常用的 Grails 标记。它将遍历列表中的每个 项。要查看它的效果,请在文本编辑器中打开 grails-app/views/trip/list.gsp(如清单 3 所示):
清单 3.list.gsp 视图
1
2
3
4
5
6
7
8
9
10
<g:each in="${tripList}" status="i" var="trip">
  <tr class="${(i % 2) == 0 ? 'even' : 'odd'}">
    <td><link action="show" id="${trip.id}">${trip.id?.encodeAsHTML()}</g:link></td>
    <td>${trip.airline?.encodeAsHTML()}</td>
    <td>${trip.name?.encodeAsHTML()}</td>
    <td>${trip.city?.encodeAsHTML()}</td>
    <td>${trip.startDate?.encodeAsHTML()}</td>
    <td>${trip.endDate?.encodeAsHTML()}</td>
  </tr>
</g:each>




<g:each> 标记的 status 属性是个简单的计数器字段(请注意这个值用在下一行的 ternary 语句中,用来将 CSS 样式设为 even 或        odd)。var 属性允许命名用来保存当前项的变量。如果将名称改为 foo,那么需要将后面的行改为        ${foo.airline?.encodeAsHTML()},依次类推(?. 操作符是 Groovy 用来避免        NullPointerException 的方法。它可以快捷地表示 “只有在 airline 不为 null 时才调用 encodeAsHTML() 方法,否则返回空字符串”)。
另一个常用 Grails 标记是 <g:link>。顾名思义,它的作用是构建一个 HTML        <a href> 链接。当然也可以直接使用 <a href> 标记,但是这个方便的标记还接受属性,例如 action、id 和 controller。如果只考虑 href 的值而不考虑它前后的 anchor 标记,那么可以改用        <g:createLink> 。在 list.gsp 顶部能看到返回链接的第三个标记:        <g:createLinkTo>。这个标记接受 dir 和 file 属性而不是 controller、action 和 id 属性。清单 4 显示了 link 和        createLinkTo 的作用:
清单 4. link 标记 vs. createLinkTo 标记
1
2
3
4
<div class="nav">
  <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
  <span class="menuButton"><link class="create" action="create">New Trip</g:link></span>
</div>




注意,在清单 4 中,可以交替使用两种不同的形式调用 Grails 标记 — 一种是在尖括号内包含标记,一种是仿效方法调用在大括号内包含标记。当在另一个标记的属性中调用方法时,大括号表示法(正式名称为表达式语言或 EL 语法)更合适。
在 list.gsp 下面的几行,能够看到另一个流行的 Grails 标记:<g:if>,如清单 5 所示。在这个示例中,它的意思是 “如果 flash.message 属性不为 null,就显示它。”
清单 5. <g:if> 标记
1
2
3
4
<h1>Trip List</h1>
<if test="${flash.message}">
  <div class="message">${flash.message}</div>
</g:if>




在浏览生成的视图时,还会看到其他许多 Grails 标记。<g:paginate> 标记在数据库包含的 Trip 比当前显示的 10 条记录多时,显示 “前一个” 和 “下一个” 链接。<g:sortable> 标记使列标题变为可单击,从而支持排序。看看其他 GSP 页面中与 HTML 表单有关的标记,例如 <g:form> 和 <g:submit>。Grails 的联机文档中列出了所有可用的 Grails 标记,并提供了它们的用法示例(请参阅 )。
返回列表