Grails是典型的MVC,其中M(Domain Class)和C(Controller)我们都已经接触过了,现在来看看最后的View。在Grails中,View一般是对应的GSP。
工程的GSP都是存放在工程目录下的grails-app/views中,在Controller一节,我们已经谈过如何定位它的问题,在这里就不再重复。对于初次接触GSP的读者来说,简单地套用JSP语法即可:
  • <%%>中书写Groovy代码
  • <%=%>输出
  • 表达式:${...}
这里需要注意的是,缺省表达式不会进行转义。如果想,那么在Config.groovy中添加:grails.views.default.codec="html"。至于转义的作用,在以后的安全部分会进行讨论。
和JSP一样,GSP中也有一系列的预定义变量:
  • application:javax.servlet.ServletContext
  • applicationContext :Spring ApplicationContext
  • flash:flash对象
  • grailsApplication:GrailsApplication
  • out:response
  • params:请求参数
  • request:HttpServletRequest
  • response:HttpServletResponse
  • session:HttpSession
  • webRequest:GrailsWebRequest
对于这些变量的使用自不必多言,使用完全和JSP的套路一样。当然,GSP中也不会少了标签的份。Grails自身提供了大量的标签供开发者使用,详细的标签库名录请参见开发文档。
GSP标签除了一般的JSP风格用法,还有其他使用方式。总结起来:
  • 常规用法:<img src="<g:resource dir="p_w_picpaths" file="logo.jpg" />" />
  • 作为表达式:<img src="${resource(dir:'p_w_picpaths', file:'logo.jpg')}" />
  • 作为方法:def p_w_picpathLocation = resource(dir:"p_w_picpaths", file:"logo.jpg")
  • 使用名字空间:def p_w_picpathLocation = g.resource(dir:"p_w_picpaths", file:"logo.jpg")
GSP中还有template的概念:
  • 它是可重用的View片断
  • 它是GSP文件,名字以下划线开始,如“_bookTemplate.gsp”
  • 定位规则同View
template文件内容示例:
<div class="book" id="${book?.id}">
   <div>Title: ${book?.title}</div>
   <div>Author: ${book?.author?.name}</div>
</div>
在页面中使用template,引用时按名字去引用不要加下划线:
  • 一般:<g:render template="bookTemplate" model="[book:myBook]" />
  • 循环:<g:render template="bookTemplate" var="book" collection="${bookList}" />
在Controller中的使用:
  • render方法,直接写到response中:render(template:"bookTemplate", model:[book:b])
  • render标签,返回String:g.render(template:"bookTemplate", model:[book:b])
Template由于使用频繁,因此Grails给它建立单独的名字空间:tmpl。如:<g:render template="bookTemplate" model="[book:myBook]" />可简写成:<tmpl:bookTemplate book="${myBook}" />。
GSP对于页面布局也提供了支持,这就是Layout。它是以Sitemesh为基础,同样也是GSP文件,文件位于grails-app/views/layouts。Layout定义涉及3个主要元素:
  • layoutTitle,输出目标页Title
  • layoutHead,输出目标页Head
  • layoutBody,输出目标页Body
使用示例:
<html>
      <head>
          <title><g:layoutTitle default="An example decorator" /></title>
          <g:layoutHead />
      </head>
      <body οnlοad="${pageProperty(name:'body.            <div class="menu">
                 <!--my common menu goes here?
                 <div class="body">
                      <g:layoutBody />
                 </div>
            </div>
      </body>
</html>
其中,pageProperty,可用于输出目标页的某个属性。
对于Layout的使用,4种方式。
法1:在<head>之间使用<meta name="layout" content="main"></meta>。
法2:使用规约。通过存放位置来确定。当有多个匹配时,以详细的优先使用:
  • 应用于Controller:grails-app/views/layouts/controller-name.gsp
  • Action:grails-app/views/layouts/controller-name/action-name.gsp
法3:使用Controller的layout属性
class BookController {
    //该controller相关的所有view都将使用
    //grails-app/views/layouts/customer.gsp为Layout
    static layout = 'customer'
    def list = {  … }
}
法4,使用<g:applyLayout>,对内容区域、URL、template应用模板:
<g:applyLayout name="myLayout" template="bookTemplate" collection="${books}" />
<g:applyLayout name="myLayout" url="http://www.google.com" />
<g:applyLayout name="myLayout">
        The content to apply a layout to
</g:applyLayout>
在GSP中使用inlcude,有以下的方式:
  • 作为标签:<g:include controller="book" action="list"/>
  • 或者跟applyLayout合用:
    <g:applyLayout name="myLayout">
           <g:include controller="book" action="list"/>
        </g:applyLayout>
  • 作为方法调用:def content = include(controller:"book", action:"list")
在Layout中,我们还可以使用内容块,这将让我们可以更灵活地使用layout。它的使用分成2部分:
在Layout中使用<g:pageProperty />预留:
<div id="left1">
    <g:pageProperty name="page.layout1.left.content"/>
</div>
<div id="top2">
    <div id="toolbar"></div>
</div>
<div id="center2">
    <g:pageProperty name="page.layout2.center.content"/>
</div>
<div id="bottom2">
    <g:pageProperty name="page.layout2.bottom.content"/>
</div>
<g:pageProperty name="page.other"/>
在内容页中使用<content>填充:
<content tag="layout1.left.content"> <!-- 注意没有page前缀 -->
    <div id="toc"></div>
</content>
<content tag="layout2.center.content">
    <div id="terminal"></div>
</content>
<content tag="layout2.bottom.content">
    <div id="helpstone"></div>
</content>
<content tag="other">
    <div id="how-to" style="text-align:left;">
        ...
    </div>
</content>
在GSP部分的最后,让我们来看看如何在GSP中创建标签库。GSP的标签库是一个Groovy文件,每个闭包代表一个标签。使用grails create-tag-lib来创建,标签位于grails-app/taglib。对于每个标签定义都有两个参数:
  • attrs,标签属性;
  • body,标签体。body()返回标签体内容,body(map)给标签体传参数,标签体则使用之。
标签示例:
class SimpleTagLib {
    def simple = { attrs, body ->
        //直接输出,对于这种情况,可直接输出到out
        //省去body()
        out << body() << attrs.happy == 'true' ? " :-)" : " :-("
    }
}
<!-- 使用 -->
<g:simple happy="...">...</g:simple>
如果想要动态生成传给body的参数键值,这要使用: body((var):num),这将会把val的值作为key。
在Tag中同样有相应的预定义变量:
  • actionName,当前执行的Action名
  • controllerName,当前执行的Controller名
  • flash,flash对象
  • grailsApplication,GrailsApplication
  • out,response writer
  • pageScope,GSP的pageScope对象
  • params,request parameters
  • pluginContextPath,包含tag的插件context path
  • request
  • response
  • servletContext
  • session
典型标签例子:
  • 简单
    def dateFormat = { attrs, body ->
         out << new java.text.SimpleDateFormat(attrs.format).format(attrs.date)
        }
  • 逻辑
    def isAdmin = { attrs, body ->
            def user = attrs['user']
            if(user != null && checkUserPrivs(user)) {
               out << body()
            }
        }
  • 循环
    def repeat = { attrs, body ->
            attrs.times?.toInteger().times { num ->
                out << body(num)
            }
        }
标签库缺省的名字空间为g,你同样可以自定义名字空间:
class SimpleTagLib {
    static namespace = "my"
    ...
}
在GSP中可以使用JSP标签,步骤:
声明:<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
使用
  • 方法1:常规使用。<fmt:formatNumber value="${10}" pattern=".00"/>
  • 方法2:按方法调用,这是额外的好处。${fmt.formatNumber(value:10, pattern:".00")}
从1.2起,标签缺省返回:org.codehaus.groovy.grails.web.util.StreamCharBuffer类型,这样可以提高页面展示的效率。之前,返回的是String。从1.2起,标签也可以直接返回对象值。