8.3 Tag Libraries - Reference Documentation
Authors: Graeme Rocher, Peter Ledbrook, Marc Palmer, Jeff Brown, Luke Daley, Burt Beckwith, Lari Hotari
Version: 3.1.4
Table of Contents
8.3 Tag Libraries
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 conventionTagLib
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 Spring Tool Suite (STS) and others autocomplete tag attributes, you should add Javadoc comments to your tag closures with@attr
descriptions. 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) } }
8.3.1 Variables and Scopes
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
8.3.2 Simple Tags
As demonstrated in the previous example it is easy to write simple tags that have no body and just output content. Another example is adateFormat
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])
}
8.3.3 Logical Tags
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:def isAdmin = { attrs, body ->
def user = attrs.user
if (user && checkUserPrivs(user)) {
out << body()
}
}
<g:isAdmin user="${myUser}"> // some restricted content </g:isAdmin>
8.3.4 Iterative Tags
Iterative tags are easy too, since you can invoke the body multiple times: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 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.
8.3.5 Tag Namespaces
By default, tags are added to the default Grails namespace and are used with theg:
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")
8.3.6 Using JSP Tag Libraries
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" %>
grails.gsp.tldScanPattern
setting. It accepts a comma separated String value. Spring's PathMatchingResourcePatternResolver is used to resolve the patterns.For example you could scan for all available tld files by adding this to application.groovy
:
grails.gsp.tldScanPattern='classpath*:/META-INF/*.tld,/WEB-INF/tld/*.tld'
build.gradle
:
runtime 'javax.servlet:jstl:1.1.2' runtime 'taglibs:standard:1.1.2'
<fmt:formatNumber value="${10}" pattern=".00"/>
${fmt.formatNumber(value:10, pattern:".00")}
8.3.7 Tag return value
A taglib can be used in a GSP as an ordinary tag or it might be used as a function in other taglibs or GSP expressions.Internally Grails intercepts calls to taglib closures. The "out" that is available in a taglib is mapped to ajava.io.Writer
implementation that writes to a buffer
that "captures" the output of the taglib call. This buffer is the return value of a tag library call when it's
used as a function.If the tag is listed in the library's static returnObjectForTags
array, then its return value will written to
the output when it's used as a normal tag. The return value of the tag lib closure will be returned as-is
if it's used as a function in GSP expressions or other taglibs.If the tag is not included in the returnObjectForTags array, then its return value will be discarded.
Using "out" to write output in returnObjectForTags is not supported.Example:
class ObjectReturningTagLib { static namespace = "cms" static returnObjectForTags = ['content'] def content = { attrs, body -> CmsContent.findByCode(attrs.code)?.content } }