class SimpleTagLib {
}
8 Tag Libraries
Version: 6.2.4
Table of Contents
8 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 convention TagLib
and place it within the grails-app/taglib
directory:
Now to create a tag create a Closure property that takes two arguments: the tag attributes and the body content:
class SimpleTagLib {
def simple = { attrs, body ->
}
}
The 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' ? " :-)" : " :-(")
}
}
As demonstrated above there is an implicit 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:
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' ? " :-)" : " :-(")
}
}
and any mandatory attributes should include the REQUIRED keyword, e.g.
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.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 name -
controllerName
- The currently executing controller name -
flash
- The flash object -
grailsApplication
- The GrailsApplication instance -
out
- The response writer for writing to the output stream -
pageScope
- A reference to the pageScope object used for GSP rendering (i.e. the binding) -
params
- The params object for retrieving request parameters -
pluginContextPath
- The context path to the plugin that contains the tag library -
request
- The HttpServletRequest instance -
response
- The HttpServletResponse instance -
servletContext
- The javax.servlet.ServletContext instance -
session
- The HttpSession instance
8.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 a dateFormat
style tag:
def dateFormat = { attrs, body ->
out << new java.text.SimpleDateFormat(attrs.format).format(attrs.date)
}
The above uses Java’s 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()}" />
With simple tags sometimes you need to write HTML mark-up to the response. One approach would be to embed the content directly:
def formatBook = { attrs, body ->
out << "<div id=\"${attrs.book.id}\">"
out << "Title : ${attrs.book.title}"
out << "</div>"
}
Although this approach may be tempting it is not very clean. A better approach would be to reuse the render tag:
def formatBook = { attrs, body ->
out << render(template: "bookTemplate", model: [book: attrs.book])
}
And then have a separate GSP template that does the actual rendering.
8.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()
}
}
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:
<g:isAdmin user="${myUser}">
// some restricted content
</g:isAdmin>
8.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)
}
}
In this example we check for a 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>
Notice how in this example we use the implicit 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)
That value is then passed as the default variable 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)
}
}
Here we check if there is a 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>
Notice how we use the 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.5 Tag Namespaces
By default, tags are added to the default Grails namespace and are used with the 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 ->
//...
}
}
Here we have specified a namespace
of my
and hence the tags in this tag lib must then be referenced from GSP pages like this:
<my:example name="..." />
where the prefix is the same as the value of the static 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")
This works from GSP, controllers or tag libraries
8.6 Using JSP Tag Libraries
In addition to the simplified tag library mechanism provided by GSP, you can also use JSP tags from GSP.
In order to use JSP support you must ensure you have the grails-web-jsp
dependency on your classpath by adding it to your build.gradle
file:
dependencies {
//...
runtimeOnly "org.grails:grails-web-jsp:6.2.4"
}
Then you will need to declare the JSP taglib to use with the taglib
directive at the top of your GSP file:
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
Besides this you have to configure Grails to scan for the JSP tld files.
This is configured with the 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.yml
:
grails:
gsp:
tldScanPattern: 'classpath*:/META-INF/*.tld,/WEB-INF/tld/*.tld'
JSTL standard library is no longer added as a dependency by default. In case you are using JSTL, you should also add these dependencies to build.gradle
:
dependencies {
//...
runtimeOnly 'javax.servlet:jstl:1.1.2'
runtimeOnly 'taglibs:standard:1.1.2'
}
Then you can use JSP tags like any other tag:
<fmt:formatNumber value="${10}" pattern=".00"/>
With the added bonus that you can invoke JSP tags like methods:
${fmt.formatNumber(value:10, pattern:".00")}
8.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 a java.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 be 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
}
}
Given this example cms.content(code: 'something')
call in another taglib or GSP expression would return the value CmsContent.content
directly to the caller without
wrapping the return value in a buffer. It might be worth doing so also because of performance optimization reasons. There is no need to wrap the
tag return value in an output buffer in such cases.