Create a simple Tag Library in Grails

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.

Sorry, comments for this entry are closed at this time.