Grails, taglib, educational technologies

Creating custom GSP tags and tag libraries in Grails

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:

1
2
3
4
5
<!-- 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>

The output:

1
2
3
<p>The number is 1</p>
<p>The number is 2</p>
<p>The number is 3</p>

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:

1
2
3
4
5
6
7
8
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
    }
}

Let's create the 'renderList' action which takes a list of objects and renders its values using styled 'span' elements:

1
2
3
4
5
6
7
8
9
10
11
12
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>"
       }
   }
}

Now, define the renderList tag on a GSP page using the closure name:

1
<g:renderList values="[1, 2, 3]" />

This results in the following output:

1
2
3
<span class="element"> 1 </span>
<span class="element"> 2 </span>
<span class="element"> 3 </span>

Referencing a tag variable

You can pass variables to a content enclosed by a tag and use the enclosed content as a template:

1
2
// inside a tag action
out << body((variableName):variableValue) // passes the variable value to the body closure
1
2
3
4
<!-- on a JSP page -->
<namespace:tagname>
    <p>${variableName}</p> <!-- references the variable by name and renders its value -->
</namespace:tagname>

For example, here is the 'iterator' action which repeats its enclosed content, this action defines the 'i' variable that keeps the iteration number:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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)
       }
   }

   // ...
}

The GSP page code:

1
<g:iterate times="3" iterator="j">Iterated ${j} times</g:iterate>

or the same code without predefined iterator, when the variable i is used by default:

1
<g:iterate times="3">Iterated ${i} times</g:iterate>

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:

1
2
3
4
class ExampleTagLib {

    static namespace = "namespaceName"
}

For instance:

1
2
3
4
5
6
7
class UtilTagLib {

    /** The tag library namespace. */
    static namespace = "admin"

    def iterate = { //...
}

Now, to use the iterate tag action, you should specify the admin namespace as the tag prefix:

1
<admin:iterate... >

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.

The post is written for the Grails version 1.3.7

Looking to hire a software developer?
Don't hesitate to contact us.

Comments