grails layout

 

http://justjavac.iteye.com/blog/702175

6.2 Groovy Server Pages

Groovy Servers Pages (或者简写为 GSP)Grails的视图技术。它被设计成像ASP和JSP这样被使用者熟悉的技术,但更加灵活和直观.

GSP存在于Grails的grails-app/views目录中,他们通常会自动渲染(通过规约),或者像这样通过render方法:

 

render(view:"index")

GSP使典型的混合标记和GSP标签,辅助页面渲染.

 

虽然,它可能会在你的GSP页面中内置Groovy逻辑,Although it is possible to have Groovy logic embedded in your GSP and doing this will be covered in this document the practice is strongly discouraged. Mixing mark-up and code is a bad thing and most GSP pages contain no code and needn't do so.

一个GPS通常拥有一个"model",它是变量集被用于视图渲染。通过一个控制器model被传递到GSP视图。例如,考虑下列控制器的操作:

 

def show = {
	[book: Book.get(params.id)]
}

这个操作将查找一个book实体,并创建一个包含关键字为book的model,这个关键字可在随后的GSP视图中应用:

 

<%=book.title%>

6.2.1 GSP基础

在下一节,我们将通过GSP基础知识让你知道它能做什么。首先,我们将涵盖基础语法,对于JSP和ASP用户是非常熟悉的.

GSP支持使用<% %>来嵌入Groovy代码(这是不推荐的):

 

<html>
   <body>
     <% out << "Hello GSP!" %>
   </body>
</html>

同样,你可以使用<%= %>语法来输出值:

 

<html>
   <body>
     <%="Hello GSP!" %>
   </body>
</html>

GSP同样支持服务器端JSP样式注释,像下列示例显示的这样:

 

<html>
   <body>
	 <%-- This is my comment --%>
     <%="Hello GSP!" %>
   </body>
</html>

6.2.1.1 变量与作用域

在 <% %> 中你当然可以声明变量:

 

<% now = new Date() %>

然后,在页面中的之后部分可以重复使用 :

 

<%=now%>

然而, 在GSP中存在着一些预先定义的变量,包括:

 

6.2.1.2 逻辑和迭代

使用 <% %> 语法,你当然可以使用这样的语法进行嵌套循环等等操作:

 

<html>
   <body>
      <% [1,2,3,4].each { num -> %>
         <p><%="Hello ${num}!" %></p>
      <%}%>
   </body>
</html>

同样可以分支逻辑:

 

<html>
   <body>
      <% if(params.hello == 'true' )%>	
      <%="Hello!"%>
      <% else %>
      <%="Goodbye!"%>
   </body>
</html>

6.2.1.3 页面指令

GSP同样支持少许的JSP样式页面指令.

import指令允许在页面中导入类。然而,它却很少被使用,因为Groovy缺省导入和GSP 标签:

 

<%@ page import="java.awt.*" %>

GSP同样支持contentType@ 指令:

 

<%@ page contentType="text/json" %>

contentType@指令允许GSP使用其他的格式来渲染.

6.2.1.4 表达式

尽管GSP也支持 <%= %> 语法,而且很早就介绍过,但在实际当中却很少应用,因为此用法主要是为ASP和 、JSP开发者所保留的。 而GSP的表达式跟JSP EL表达式很相似的,跟Groovy GString的 ${expr} 用法也很像:

 

<html>
  <body>
    Hello ${params.name}
  </body>
</html>

尽管如此,跟JSP EL不同的是, 你可以在${..}括号中使用Groovy表达式.${..}中的变量缺省情况下是转义,因此变量的任何HTML字符串内容被直接输出到页面,要减少这种Cross-site-scripting (XSS)攻击的风险, 你可以设置grails-app/conf/Config.groovy中的grails.views.default.codec为HTML转化方式:

 

grails.views.default.codec='html'

其他可选的值是'none' (缺省值)和'base64'.

6.2.2 GSP标签

现在,JSP遗传下来的缺点已经被取消,下面的章节将涵盖GSP的内置标签,它是定义GSP页面最有利的方法.

 

标签库 部分涵盖怎么添加你自己的定制标签库.

所有GSP内置标签以前缀g:开始。 不像JSP,你不需要指定任何标签库的导入.假如,一个标签以g:开始,它被自动认为是一个GSP标签.一个GPS标签的示例看起来像这样:

 

<g:example />

GSP标签同样可以拥有主体,像这样:

 

<g:example>
   Hello world
</g:example>

表达式被传递给GSP标签属性,假如没有使用表达式,将被认为是一个String值:

 

<g:example attr="${new Date()}">
   Hello world
</g:example>

Maps同样能被传递给GSP标签属性,通常使用一个命名参数样式语法:

 

<g:example attr="${new Date()}" attr2="[one:1, two:2, three:3]">
   Hello world
</g:example>

注意,对于String类型属性值,你必须使用单引号:

 

<g:example attr="${new Date()}" attr2="[one:'one', two:'two']">
   Hello world
</g:example>

在介绍完基本的语法之后,下面我们来讲解Grails中默认提供的标签.

 

6.2.2.1 变量与作用域

变量可以在GSP中使用 set 标签来定义:

 

<g:set var="now" value="${new Date()}" />

这里, 我们给GSP表达式结果赋予了一个名为now的变量 (简单的构建一个新的 java.util.Date 实体)。 你也可以在<g:set>主体中定义一个变量:

 

<g:set var="myHTML">
   Some re-usable code on: ${new Date()}
</g:set>

变量同样可以被放置于下列的范围内:

  • page - 当前页面范围 (默认)
  • request - 当前请求范围
  • flash - flash 作用域,因此它可以在下一次请求中有效
  • session - 用户session范围
  • application - 全局范围.

选择变量被放入的范围可以使用scope属性:

 

<g:set var="now" value="${new Date()}" scope="request" />

6.2.2.2 逻辑和迭代

GSP同样支持迭代逻辑标签,逻辑上通过使用 if , , else 和 elseif 来支持典型的分支情形。 :

 

<g:if test="${session.role == 'admin'}">
   <%-- show administrative functions --%>
</g:if>
<g:else>
   <%-- show basic functions --%>
</g:else>

GSP用each each和while标签来处理迭代:

 

<g:each in="${[1,2,3]}" var="num">
   <p>Number ${num}</p>
</g:each>

<g:set var="num" value="${1}" /> <g:while test="${num < 5 }"> <p>Number ${num++}</p> </g:while>

6.2.2.3 搜索和过滤

假如你拥有对象集合,你经常需要使用一些方法来排序和过滤他们。 GSP支持 findAll 和 grep 来做这些工作:

 

Stephen King's Books:
<g:findAll in="${books}" expr="it.author == 'Stephen King'">
     <p>Title: ${it.title}</p>
</g:findAll>

expr属性包含了一个Groovy表达式,它可以被当作一个过滤器来使用。谈到过滤器,grep标签通过类来完成与过滤器类似的工作:

 

<g:grep in="${books}" filter="NonFictionBooks.class">
     <p>Title: ${it.title}</p>
</g:grep>

或者使用一个正则表达式:

 

<g:grep in="${books.title}" filter="~/.*?Groovy.*?/">
     <p>Title: ${it}</p>
</g:grep>

上面的示例同样有趣,因为它使用了GPath.Groovy的GPath等同与XPath语言。实际上books集合是books集合的实体。不过,假设每个books拥有一个title,你可以使用表达式books.title来获取Book titles的list!

6.2.2.4 链接和资源

GSP还拥有特有的标签来帮助你管理连接到控制器和操作. link 标签允许你指定控制器和操作配对的名字,并基于URL Mappings 映射来自动完成连接。即使你去改变!一些 link 的示例如下:

 

<g:link action="show" id="1">Book 1</g:link>
<g:link action="show" id="${currentBook.id}">${currentBook.name}</g:link>
<g:link controller="book">Book Home</g:link>
<g:link controller="book" action="list">Book List</g:link>
<g:link url="[action:'list',controller:'book']">Book List</g:link>
<g:link action="list" params="[sort:'title',order:'asc',author:currentBook.author]">
     Book List
</g:link>

6.2.2.5 表单和字段

表单基础

GSP支持许多不同标签来帮助处理HTML表单和字段,最基础的是form标签, form标签是一个控制器/操作所理解的正规的HTML表单标签版本。 url属性允许你指定映射到哪个控制器和操作:

 

<g:form name="myForm" url="[controller:'book',action:'list']">...</g:form>

我们创建个名为myForm的表单,它被提交到 BookControllerlist操作。除此之外,适用于所有不同的HTML属性.

 

表单字段

同构造简单的表单一样,GSP支持如下不同字段类型的定制:

  • textField - 'text'类型输入字段
  • checkBox - 'checkbox'类型输入字段
  • radio - 'radio'类型输入字段
  • hiddenField - 'hidden'类型输入字段
  • select - 处理 HTML 选择框

上面的每一个都允许GSP表达式作为值:

 

<g:textField name="myField" value="${myValue}" />

GSP同样包含上面标签的扩张助手版本, 比如radioGroup (创建一组radio标签), localeSelect, currencySelect 和timeZoneSelect(选择各自的地区区域, 货币 和时间区域). .

 

多样的提交按钮

处理多样的提交按钮这样由来已久的问题,同样可以通过Grails的actionSubmit 标签优雅的处理。它就像一个正规提交,但是,允许你指定一个可选的操作来提交:

 

<g:actionSubmit value="Some update label" action="update" />

 

6.2.2.6 标签作为方法调用

GSP标签和其他标签技术一个主要不同在于,来自 controllers(控制器) , 标签库 或者GSP 视图中的GPS标签可以被当作任意的正规标签或者当作方法被调用.

 

来自GSPs中的标签当作方法调用

当作为方法被调用时,标签的返回值被当作String实体直接被写入响应中。 因此,示例中的createLinkTo能等同的看做方法调用:

 

Static Resource: ${createLinkTo(dir:"images", file:"logo.jpg")}

当你必须在一个属性内使用一个标签时是特别有用的:

 

<img src="${createLinkTo(dir:'images', file:'logo.jpg')}" />

I在视图技术中,标签内嵌套标签的特性是不被支持的,这样变得十分混乱,往往使得像Dreamweaver这样WYSWIG的工具产生不利的效果以至于在渲染标签时:

 

<img src="<g:createLinkTo dir="images" file="logo.jpg" />" />

 

来自控制器(Controllers)和标签库的标签作为方法调用

你同样可以调用来自控制器和标签库的标签。标签可以不需要内部默认的g:namespace前缀来调用,并返回String结果:

 

def imageLocation = createLinkTo(dir:"images", file:"logo.jpg")

然而,你同样可以用命名空间前缀来避免命名冲突:

 

def imageLocation = g.createLinkTo(dir:"images", file:"logo.jpg")

假如你有一个自定义命名空间,,你可以使用它的前缀来替换(例如,使用 FCK Editor plugin:

 

def editor = fck.editor()

 

 

6.2.3 视图(View)与模板(Templates)

除了views之外, Grails还有模板的概念. 模板有利于分隔出你的视图在可维护的块中,并与 Layouts 结合提供一个高度可重用机制来构建视图.

 

模板基础

Grails使用在一个视图名字前放置一个下划线来标识为一个模板的规约。 例如,你可能有个位于grails-app/views/book/_bookTemplate.gsp的模板处理渲染Books:

 

<div class="book" id="${book?.id}">
   <div>Title: ${book?.title}</div>
   <div>Author: ${book?.author?.name}</div>
</div>

为了渲染来自grails-app/views/book视图中的一个模板,你可以使用render标签:

 

<g:render template="bookTemplate" model="[book:myBook]" />

注意,我们是怎么样使用render标签的model属性来使用传入的一个model。假如,你有多个book实体,你同样可以使用render标签为每个book渲染模板 :

 

<g:render template="bookTemplate" var="book" collection="${bookList}" />

 

共享模板

在早先的示例中,我们有一个特定于BookController模板,它的视图位于grails-app/views/book.然而,你可能想横跨你的应用来共享模板。

在这种情况下,你可以把他们放置于grails-app/views视图根目录或者位于这个位置的任何子目录,然后在模板属性在模板名字之前使用一个 /来指明相对模板路径 .例如,假如你有个名为grails-app/views/shared/_mySharedTemplate.gsp模板, 你可以像下面这样引用它:

 

<g:render template="/shared/mySharedTemplate" />

你也可以使用这个技术从任何视图或控制器(Controllers)来引用任何目录下的模板:

 

<g:render template="/book/bookTemplate" model="[book:myBook]" />

 

模板命名空间

因为模板使用如此频繁,它有一个模板命名空间, 名为tmpl, 他使模板的使用变得容易. 考虑下面例子的使用模式:

 

<g:render template="bookTemplate" model="[book:myBook]" />

这个想下面这样通过tmpl命名空间表示 :

 

<tmpl:bookTemplate book="${myBook}" />

 

在控制器(Controllers)和标签库中的模板

你同样可以使用控制器 render方法渲染模板控制器中,它对Ajax引用很有用:

 

def show = {
    def b = Book.get(params.id)
	render(template:"bookTemplate", model:[book:b])
}

在控制器(controller)中的render 方法最普通的行为是直接写入响应。 假如,你需要获得模板作为一个String的结果作为替代,你可以使用render标签:

 

def show = {
    def b = Book.get(params.id)
	String content = g.render(template:"bookTemplate", model:[book:b])
	render content
}

注意, g. 命名空间的用法,它告诉Grails我们想使用标签作为方法调用来代替render 方法.

6.2.4 使用Sitemesh布局

创建布局

Grails利用了Sitemesh,一个装饰引擎,来支持视图布局。布局位于grails-app/views/layouts 目录中。一个典型的布局如下:

 

<html>
      <head>
          <title><g:layoutTitle default="An example decorator" /></title>
          <g:layoutHead />
      </head>
      <body onload="${pageProperty(name:'body.onload')}">
            <div class="menu"><!--my common menu goes here--></menu>
                 <div class="body">
                      <g:layoutBody />
                 </div>
            </div>
      </body>
</html>

关键的元素是layoutHead, layoutTitle和layoutBody标签的用法,这里是他们所做的:

  • layoutTitle - 输出目标页面的title
  • layoutHead - 输出目标页面head标签内容
  • layoutBody - 输出目标页面body标签内容

上面的示例同样表明pageProperty tag 可被用于检查和返回目标页面的外观.

 

启用布局

这里有一些方法来启用一个布局.简单的在视图中添加meta标签:

 

<html>
    <head>
	    <title>An Example Page</title>
        <meta name="layout" content="main"></meta>
    </head>
    <body>This is my content!</body>
</html>

在这种情况下,一个名为grails-app/views/layouts/main.gsp将被用于布局这个页面。假如,我们使用来自早前部分的布局,输出看上去像下列这样:

 

<html>
      <head>
          <title>An Example Page</title>
      </head>
      <body onload="">
        <div class="menu"><!--my common menu goes here--></div>
                 <div class="body">
					This is my content!
                 </div>
      </body>
</html>

 

在控制器(Controller)中指定布局

另一种用于指定布局的方式是通过在控制器(controller)中为 "layout"属性指定布局的名字, 假如你有个这样的控制器(controller):

 

class BookController {
    static layout = 'customer'

def list = { … } }

你可以创建一个grails-app/views/layouts/customer.gsp布局,应用于所有 BookController中委派的视图 . "layout"属性值可能包含相对于grails-app/views/layouts/目录的路径结构 . 例如:

 

class BookController {
    static layout = 'custom/customer'

def list = { … } }

视图的显然可通过 grails-app/views/layouts/custom/customer.gsp 模板.

 

布局规约

第二种关联布局的方法是使用"布局规约",假如你有个这样的控制器:

 

class BookController {
    def list = {  … }
}

你可以创建一个名为grails-app/views/layouts/book.gsp的布局,根据规约,它将被应用于BookController的所有视图中。

换句话说,你可以创建一个名为grails-app/views/layouts/book/list.gsp的布局,它将只被应用于BookController中的list操作,

如果你同时使用了以上提到的两种布局的话,那当list操作被执行的时候,那么操作将根据优先级的顺序来使用布局.

 

内联布局

通过applyLayout标签Grails同样支持Sitemesh的内联布局概念。 applyLayout标签可以被用于应用一个布局到一个模板,URL或者内容的任意部分。事实上,通过"decorating"你的模板允许你更进一步的积木化你的视图结构.

一些使用示例如下:

 

<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>

 

Server-Side包含

当 applyLayout标签被以用于引用布局外内容 applying layouts to , 假如你想简单的在当前页面包含外部内容,你可以使用 include:

 

<g:include controller="book" action="list"></g:include>

你甚至可以结合 include 标签和 applyLayout 标签来添加灵活性:

 

<g:applyLayout name="myLayout">
   <g:include controller="book" action="list"></g:include>
</g:applyLayout>

最后,你也可以在控制器(controller)或标签库把include标签作为方法调用 :

 

def content = include(controller:"book", action:"list")

最后的内容有 include标签的返回值提供 .

6.2.5 Sitemesh内容块

虽然,这对于装饰全部页面非常有用,有时,你需要装饰站点的部分独自的页面。为了实现这个可以使用内容块. 在开始时,你需要使用 <content>标签分隔装饰页面 :

 

<content tag="navbar">
… draw the navbar here…
</content>
<content tag="header">
… draw the header here…
</content>
<content tag="footer">
… draw the footer here…
</content>
<content tag="body">
… draw the body here…
</content>

随后,在布局内部,你可以引用这些组件并为每个引用单个布局:

 

<html>
	<body>
		<div id="header">
			<g:applyLayout name="headerLayout"><g:pageProperty name="page.header"></g:applyLayout>
		</div>
		<div id="nav">
			<g:applyLayout name="navLayout"><g:pageProperty name="page.navbar"></g:applyLayout>
		</div>
		<div id="body">
			<g:applyLayout name="bodyLayout"><g:pageProperty name="page.body"></g:applyLayout>
		</div>
		<div id="footer">
			<g:applyLayout name="footerLayout"><g:pageProperty name="page.footer"></g:applyLayout>			
		</div>
	</body>
</html>

相关推荐