6.3 标签库 - Reference Documentation
Authors: Graeme Rocher, Peter Ledbrook, Marc Palmer, Jeff Brown, Luke Daley, Burt Beckwith
Version: null
6.3 标签库
Like Java Server Pages (JSP), GSP supports the concept of custom tag libraries. Unlike JSP, Grails' tag library mechanism is simple, elegant and completely reloadable at runtime.Quite simply, to create a tag library create a Groovy class that ends with the convention Now to create a tag create a Closure property that takes two arguments: the tag attributes and the body content:The As demonstrated above there is an implicit
跟Java Server Pages (JSP)类似,GSP支持自定义标签库的概念。而跟JSP不同的是,Grails的标签库机制是简单而优雅的,并且完全可以在运行时重新加载。要创建一个标签库是很简单的,只需要根据规约创建一个以TagLib and place it within the grails-app/taglib directory:class SimpleTagLib {}class SimpleTagLib {
def simple = { attrs, body -> }
}attrs argument is a Map of the attributes of the tag, whilst the body argument is a Closure that returns the body content when invoked:class SimpleTagLib {
def emoticon = { attrs, body ->
out << body() << (attrs.happy == 'true' ? " :-)" : " :-(")
}
}out variable that refers to the output Writer which you can use to append content to the response. Then you can reference the tag inside your GSP; no imports are necessary:<g:emoticon happy="true">Hi John</g:emoticon>
To help IDEs like SpringSource Tool Suite (STS) and others autocomplete tag attributes, you should add Javadoc comments to your tag closures with@attrdescriptions. Since taglibs use Groovy code it can be difficult to reliably detect all usable attributes.For example:and any mandatory attributes should include the REQUIRED keyword, e.g.class SimpleTagLib { /** * Renders the body with an emoticon. * * @attr happy whether to show a happy emoticon ('true') or * a sad emoticon ('false') */ def emoticon = { attrs, body -> out << body() << (attrs.happy == 'true' ? " :-)" : " :-(") } }class SimpleTagLib { /** * Creates a new password field. * * @attr name REQUIRED the field name * @attr value the field value */ def passwordField = { attrs -> attrs.type = "password" attrs.tagName = "passwordField" fieldImpl(out, attrs) } }
TagLib结尾的Groovy类,并且放到grails-app/taglib下边就好了:class SimpleTagLib {}class SimpleTagLib {
def simple = { attrs, body -> }
}attrs参数是此标签的属性,类型为映射(Map),而body参数是一个闭包,它在被调用的时候将返回一个主体内容:class SimpleTagLib {
def emoticon = { attrs, body ->
out << body() << (attrs.happy == 'true' ? " :-)" : " :-(")
}
}out变量将引用Writer输出器,用以往响应中追加内容。因此,你可以在不导入任何东西的情况下,于你的GSP内使用标签:<g:emoticon happy="true">Hi John</g:emoticon>
为了有助于像SpringSource Tool Suite (STS)这样的IDE来自动补齐标签属性,你应该在Javadoc注释中增加标签闭包的@attr描述。因为标签库也是Groovy代码,因此不能保证检测到的所有属性都是准确可靠的。比如:并且,任何必须的属性都应该包含REQUIRED关键字,比如:class SimpleTagLib { /** * Renders the body with an emoticon. * * @attr happy whether to show a happy emoticon ('true') or * a sad emoticon ('false') */ def emoticon = { attrs, body -> out << body() << (attrs.happy == 'true' ? " :-)" : " :-(") } }class SimpleTagLib { /** * Creates a new password field. * * @attr name REQUIRED the field name * @attr value the field value */ def passwordField = { attrs -> attrs.type = "password" attrs.tagName = "passwordField" fieldImpl(out, attrs) } }
6.3.1 变量和作用域
Within the scope of a tag library there are a number of pre-defined variables including:
在一个标签库的作用域内,已经预定义了一些变量,它们包括:
actionName- The currently executing action namecontrollerName- The currently executing controller nameflash- The flash objectgrailsApplication- The GrailsApplication instanceout- The response writer for writing to the output streampageScope- A reference to the pageScope object used for GSP rendering (i.e. the binding)params- The params object for retrieving request parameterspluginContextPath- The context path to the plugin that contains the tag libraryrequest- The HttpServletRequest instanceresponse- The HttpServletResponse instanceservletContext- The javax.servlet.ServletContext instancesession- The HttpSession instance
actionName- 当前正在运行的操作名称controllerName- 当前正在运行的控制器名称flash- flash对象grailsApplication- GrailsApplication实例out- 响应输出器,用于将内容写到输出流中pageScope- 一个pageScope对象引用,用于GSP的渲染(比如绑定)params- 用于接受请求参数的params对象pluginContextPath- 包含标签库的插件上下文路径request- HttpServletRequest实例response- HttpServletResponse实例servletContext- javax.servlet.ServletContext实例session- HttpSession实例
6.3.2 简单标签
As demonstrated it the previous example it is easy to write simple tags that have no body and just output content. Another example is a The above uses Java's With simple tags sometimes you need to write HTML mark-up to the response. One approach would be to embed the content directly:Although this approach may be tempting it is not very clean. A better approach would be to reuse the render tag:And then have a separate GSP template that does the actual rendering.
正如以前示例所演示的那样,要写一个只输出内容而没有主体(body)的标签是很容易的。另外的一个示例是dateFormat style tag:def dateFormat = { attrs, body ->
out << new java.text.SimpleDateFormat(attrs.format).format(attrs.date)
}SimpleDateFormat class to format a date and then write it to the response. The tag can then be used within a GSP as follows:<g:dateFormat format="dd-MM-yyyy" date="${new Date()}" />def formatBook = { attrs, body ->
out << "<div id="${attrs.book.id}">"
out << "Title : ${attrs.book.title}"
out << "</div>"
}def formatBook = { attrs, body ->
out << render(template: "bookTemplate", model: [book: attrs.book])
}dateFormat风格的标签:def dateFormat = { attrs, body ->
out << new java.text.SimpleDateFormat(attrs.format).format(attrs.date)
}SimpleDateFormat类来格式化一个日期,并且将它写回到响应中。然后标签就可以在GSP中像下面所示那样使用:<g:dateFormat format="dd-MM-yyyy" date="${new Date()}" />def formatBook = { attrs, body ->
out << "<div id="${attrs.book.id}">"
out << "Title : ${attrs.book.title}"
out << "</div>"
}def formatBook = { attrs, body ->
out << render(template: "bookTemplate", model: [book: attrs.book])
}6.3.3 逻辑标签
You can also create logical tags where the body of the tag is only output once a set of conditions have been met. An example of this may be a set of security tags:The tag above checks if the user is an administrator and only outputs the body content if he/she has the correct set of access privileges:
你也可以创建一个逻辑标签,一旦一组条件表达式满足,就输出标签的主体。一组安全标签的示例如下:def isAdmin = { attrs, body ->
def user = attrs.user
if (user && checkUserPrivs(user)) {
out << body()
}
}<g:isAdmin user="${myUser}"> // some restricted content </g:isAdmin>
def isAdmin = { attrs, body ->
def user = attrs.user
if (user && checkUserPrivs(user)) {
out << body()
}
}<g:isAdmin user="${myUser}"> // some restricted content </g:isAdmin>
6.3.4 迭代标签
Iterative tags are easy too, since you can invoke the body multiple times:In this example we check for a Notice how in this example we use the implicit That value is then passed as the default variable Here we check if there is a Notice how we use the
因为你可以多次调用主体(body),所以迭代标签也是很容易的:def repeat = { attrs, body ->
attrs.times?.toInteger()?.times { num ->
out << body(num)
}
}times attribute and if it exists convert it to a number, then use Groovy's times method to iterate the specified number of times:<g:repeat times="3"> <p>Repeat this 3 times! Current repeat = ${it}</p> </g:repeat>
it variable to refer to the current number. This works because when we invoked the body we passed in the current value inside the iteration:out << body(num)
it to the tag. However, if you have nested tags this can lead to conflicts, so you should should instead name the variables that the body uses:def repeat = { attrs, body ->
def var = attrs.var ?: "num"
attrs.times?.toInteger()?.times { num ->
out << body((var):num)
}
}var attribute and if there is use that as the name to pass into the body invocation on this line:out << body((var):num)Note the usage of the parenthesis around the variable name. If you omit these Groovy assumes you are using a String key and not referring to the variable itself.Now we can change the usage of the tag as follows:
<g:repeat times="3" var="j"> <p>Repeat this 3 times! Current repeat = ${j}</p> </g:repeat>
var attribute to define the name of the variable j and then we are able to reference that variable within the body of the tag.
def repeat = { attrs, body ->
attrs.times?.toInteger()?.times { num ->
out << body(num)
}
}times属性,如果存在呢,就将其转换为一个数字,然后使用Groovy的times方法来迭代给定的次数:<g:repeat times="3"> <p>Repeat this 3 times! Current repeat = ${it}</p> </g:repeat>
it变量来引用当前的数字。此种方式是有效的,因为在迭代内部,我们将当前值传给了正在调用的主体(body):out << body(num)
it变量传给了标签。但是,如果你有嵌套的标签的话,那么这将会导致冲突,因此你应该给给调用的主体变量命名:def repeat = { attrs, body ->
def var = attrs.var ?: "num"
attrs.times?.toInteger()?.times { num ->
out << body((var):num)
}
}var属性,如果有,那么将使用其值作为变量名称传递给正在调用的主体,如下所示:out << body((var):num)注意!变量名称两边的括号。如果你忽略它们,那么Groovy将会认为你正在使用一个String类型的键,而不是变量本身。现在你可以修改标签的使用方法了,如下所示:
<g:repeat times="3" var="j"> <p>Repeat this 3 times! Current repeat = ${j}</p> </g:repeat>
var属性来将变量名称定义为j,然后就可以在标签的主体内来引用此变量了。 tag.
6.3.5 标签命名空间
By default, tags are added to the default Grails namespace and are used with the Here we have specified a where the prefix is the same as the value of the static This works from GSP, controllers or tag libraries
一般情况下,标签使用Grails的缺省命名空间,并且在GSP页面中使用g: prefix in GSP pages. However, you can specify a different namespace by adding a static property to your TagLib class:class SimpleTagLib {
static namespace = "my" def example = { attrs ->
…
}
}namespace of my and hence the tags in this tag lib must then be referenced from GSP pages like this:<my:example name="..." />namespace property. Namespaces are particularly useful for plugins.Tags within namespaces can be invoked as methods using the namespace as a prefix to the method call:out << my.example(name:"foo")g:前缀。但是你也可以通过在TagLib类中增加一个静态属性来指定另外一个命名空间:class SimpleTagLib {
static namespace = "my" def example = { attrs ->
…
}
}namespace指定为my,因此此标签库的标签在GSP页面中必须像如下所示那样引用:<my:example name="..." />namespace的值是一样的。命名空间对插件来说特别有用。带命名空间的标签也可以以方法的方式调用,需要将其命名空间作为前缀赋给方法调用:out << my.example(name:"foo")6.3.6 使用JSP标签库
In addition to the simplified tag library mechanism provided by GSP, you can also use JSP tags from GSP. To do so simply declare the JSP to use with thetaglib directive:<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %><fmt:formatNumber value="${10}" pattern=".00"/>${fmt.formatNumber(value:10, pattern:".00")}6.3.7 标签的返回值
Since Grails 1.2, a tag library call returns an instance oforg.codehaus.groovy.grails.web.util.StreamCharBuffer class by default.
This change improves performance by reducing object creation and optimizing buffering during request processing.
In earlier Grails versions, a java.lang.String instance was returned.Tag libraries can also return direct object values to the caller since Grails 1.2..
Object returning tag names are listed in a static returnObjectForTags property in the tag library class.Example:
class ObjectReturningTagLib {
static namespace = "cms"
static returnObjectForTags = ['content'] def content = { attrs, body ->
CmsContent.findByCode(attrs.code)?.content
}
}
