September 12, 2007 – 6:00 pm
I presented a quick and dirty introduction to Tag Libraries at the Groovy Users Group of MN this week. It’s a little contrived (I’m not sure you would actually write a tag to do this), but it shows how to build a simple tag library and also how to test it. This example uses Grails version 0.6 and the full source for the example can be downloaded here.
Setup the domain model
grails create-app bookstore
cd bookstore
grails create-domain-class Book
grails create-domain-class Author
grails create-domain-class Translation
Edit the domain classes and add some properties:
Author.groovy
class Author {
String name
}
Translation.groovy
class Translation {
String language
}
Book.groovy
class Book {
String title
static hasMany = [authors:Author, translations:Translation]
}
Bootstrap some data
conf/BootStrap.groovy
class BootStrap {
def init = { servletContext ->
def book = new Book(title:"Guide to Tag Libraries")
book.addToAuthors(new Author(name:'Mike'))
book.addToAuthors(new Author(name:'Robin'))
book.addToTranslations(new Translation(language:'English'))
book.addToTranslations(new Translation(language:'Spanish'))
book.save()
}
def destroy = {
}
}
Generate scaffolding and run the app
grails generate-all Author
grails generate-all Translation
grails generate-all Book
grails run-app
Browse to http://localhost:8080/bookstore/book/show/1

By default, Grails uses the toString() method to label the associated Authors and Translations, e.g. “Author : 1″
Generated scaffolding code for grails-app/views/book/show.gsp:
<tr class="prop">
<td valign="top" class="name">Authors:</td>
<td valign="top" style="text-align:left;" class="value">
<ul>
<g:each var="a" in="${book.authors}">
<li><g:link
controller="author"
action="show"
id="${a.id}">${a}</g:link>
</li>
</g:each>
</ul>
</td>
</tr>
Create a tag library to display associated properties
grails create-tag-lib Bookstore
Edit the generated file at grails-app/taglib/BookstoreTagLib.groovy. As a first pass, create a closure that outputs a line item utilizing the toString() method of each item in the collection
class BookstoreTagLib {
def eachInCollection = { attrs ->
attrs['collection'].each{
out << "<li>${it}</li>"
}
}
}
Edit the generated scaffolding code for grails-app/views/book/show.gsp to utilize the taglib
<td valign="top" style="text-align:left;" class="value">
<ul>
<g:eachInCollection collection="${book.authors}"/>
</ul>
</td>
Refresh http://localhost:8080/bookstore/book/show/1

Enhance the closure in the tag library to accept an optional attribute called “property” to display something more meaningful than “Author: 1″, i.e. display the “name” property instead…
class BookstoreTagLib {
def eachInCollection = { attrs ->
def propertyName = attrs['property']
attrs['collection'].each{
if (propertyName){
out << "<li>${it[propertyName]}</li>"
} else {
out << "<li>${it}</li>"
}
}
}
}
Modify show.gsp to pass the “property” parameter
<g:eachInCollection collection="${book.authors}" property="name"/>
Refresh http://localhost:8080/bookstore/book/show/1

Sample Unit Test
It’s easy to do assertions - invoking the closure on the taglib returns a String which can compared against an expected return value
test/integration/BookstoreTagLibTests.groovy
class BookstoreTagLibTests extends GroovyTestCase {
def taglib
def book
void setUp(){
taglib = new BookstoreTagLib()
book = new Book()
}
void testEachInCollectionNoPropertyName() {
book.addToAuthors(new Author(id:1))
assertEquals("<li>Author : 1</li>",
taglib.eachInCollection([collection: book.authors]))
}
void testEachInCollectionPropertyName() {
book.addToAuthors(new Author(name:'UnitTest1'))
assertEquals("<li>UnitTest1</li>",
taglib.eachInCollection([collection: book.authors, property:'name']))
}
}
Enhance the tag library further to support linking - by calling an existing tag library as a method!
Add a new closure in grails-app/taglib/BookstoreTagLib.groovy
def linkEachInCollection = { attrs ->
def propertyName = attrs['property']
def controllerName = attrs['controller']
attrs['collection'].each{
def prop
if (propertyName){
prop = it[propertyName]
} else {
prop = it.id
}
out << "<li>" +
link([controller:controllerName, action:'show', id:it.id]){prop} +
"</li>"
}
}
Change tag being called in show.gsp to linkEachInCollection
<g:linkEachInCollection collection="${book.authors}" property="name" controller="author"/>
Refresh http://localhost:8080/bookstore/book/show/1
Now the list of authors are linked!

Apply the tag library change to the Translations collection in show.gsp:
<g:linkEachInCollection collection="${book.translations}"
property="language" controller="translation"/>
Refresh http://localhost:8080/bookstore/book/show/1
Now the list of translations have meaningful labels and are linked!

Built in tag libraries
Don’t forget that Grails already has many built in tag libraries - see http://grails.codehaus.org/Tag+Library+Reference for more details.
Posted in grails | No Comments »