欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

grails layout 博客分类: grails grails 

程序员文章站 2024-03-20 08:49:34
...

 

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 eachwhile标签来处理迭代:

 

<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支持如下不同字段类型的定制:

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

 

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

GSP同样包含上面标签的扩张助手版本, 比如radioGroup (创建一组radio标签), localeSelectcurrencySelect 和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>

关键的元素是layoutHeadlayoutTitlelayoutBody标签的用法,这里是他们所做的:

  • 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>
相关标签: grails