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中存在着一些预先定义的变量,包括:
application- javax.servlet.ServletContext 实例applicationContextSpring ApplicationContext 实例flash- flash 对象grailsApplication- GrailsApplication 实例out- 响应输出流params- params 对象用于检索请求参数request- HttpServletRequest 实例response- HttpServletResponse 实例session- HttpSession 实例webRequest- GrailsWebRequest 实例
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的表单,它被提交到 BookController的list操作。除此之外,适用于所有不同的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- 输出目标页面的titlelayoutHead- 输出目标页面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>