Grails tag libraries are designed to provide content formatting capabilities right on a GSP page. The ‘tag’ is an action that can be declared in a form of an HTML element. This action can accept an enclosed content and parameters defined as the element attributes to render a modified HTML. For instance, the following built-in tag iterates and renders each element of the list object using the template specified in the content:
The post is written for the Grails version 1.3.7
<!-- tag namespace : tag name -->
<g:each
in="${[1, 2, 3]}"> <!-- tag attribute (takes the list object) -->
<p>The number is ${num}</p> <!-- tag body (provides the template) -->
</g:each>
Code language: HTML, XML (xml)
The output:
<p>The number is 1</p>
<p>The number is 2</p>
<p>The number is 3</p>
Code language: HTML, XML (xml)
This post is intended for those who do not have aby knowledge of Grails tag libraries, and looking for a way to quickly grasp the general concepts without diving into the documentation.
Creating a custom tag
Custom tag libraries are represented by classes located in the ‘grails-app/taglib’ project directory. Any property of a tag library class, assigned to a closure which takes two arguments, is a recognized as a tag action:
class ExampleTagLib { // tag library class
def tagAction = { attrs, body // tag library action
// any tag accepts a list of attributes and an enclosed content:
// attrs - a list of attributes
// body - a closure, returns the enclosed content
}
}
Code language: JavaScript (javascript)
Let’s create the ‘renderList’ action which takes a list of objects and renders its values using styled ‘span’ elements:
class UtilTagLib {
def renderList = { attrs, body ->
// reads the 'values' attribute from the attributes list
def list = attrs.values
// iterates and renders list values
list.each {
// uses the implicit 'out' variable to append content to the response
out << "<span class=\"element\"> ${it} </span>"
}
}
}
Code language: PHP (php)
Now, define the renderList
tag on a GSP page using the closure name:
<g:renderList values="[1, 2, 3]" />
Code language: HTML, XML (xml)
This results in the following output:
<span class="element"> 1 </span>
<span class="element"> 2 </span>
<span class="element"> 3 </span>
Code language: HTML, XML (xml)
Referencing a tag variable
You can pass variables to a content enclosed by a tag and use the enclosed content as a template:
// inside a tag action
out << body((variableName):variableValue) // passes the variable value to the body closure
Code language: JavaScript (javascript)
<!-- on a JSP page -->
<namespace:tagname>
<p>${variableName}</p> <!-- references the variable by name and renders its value -->
</namespace:tagname>
Code language: HTML, XML (xml)
For example, here is the ‘iterator’ action which repeats its enclosed content, this action defines the ‘i’ variable that keeps the iteration number:
class UtilTagLib {
/**
* @attr times times to iterate
* @attr iterator the name of the injected 'iterator' variable, default to 'i'
*/
def iterate = { attrs, body ->
attrs.times?.toInteger().times { it ->
// provides the 'i' variable to hold the iteration number
// or can use the variable name from the 'iterator' attribute
out << body((attrs.iterator ? attrs.iterator : "i") : it)
}
}
// ...
}
Code language: PHP (php)
The GSP page code:
<g:iterate times="3" iterator="j">Iterated ${j} times</g:iterate>
Code language: HTML, XML (xml)
or the same code without predefined iterator, when the variable i
is used by default:
<g:iterate times="3">Iterated ${i} times</g:iterate>
Code language: HTML, XML (xml)
Defining a tag library namespace
Namespaces help to group tag actions and isolate tag libraries in order to avoid naming conflicts. You can define a custom namespace with the static namespace
property:
class ExampleTagLib {
static namespace = "namespaceName"
}
Code language: JavaScript (javascript)
For instance:
class UtilTagLib {
/** The tag library namespace. */
static namespace = "admin"
def iterate = { //...
}
Code language: JavaScript (javascript)
Now, to use the iterate
tag action, you should specify the admin
namespace as the tag prefix:
<admin:iterate... >
Code language: HTML, XML (xml)
Conclusion
Grails tag libraries are very flexible, and allow to access most of the application scopes or even load data from a database. The only thing to remember here is that they are invoked on a GSP page, and GSP pages are not intended for data loading and processing. Such a heavy lifting should be done by services and controllers, while content formatting must be left to tag libraries which are way more readable than the embedded code blocks.