Grails: Custom Parent/Child Aware Tags

grails_logo

Recently, I needed to create a special tag in Grails which would render specific children in a way that is aware of their order. I wanted to stick entirely with markup (not passing an array to a tag), and also wanted to avoid putting lots of logic inside a .tag file. While it was possible to write a tag lib which first parsed the children in a "non-render" mode, I preferred a solution which did not require doing my own parsing before render. The solution I came up with isn't the most flexible, but it got the job done in a relatively clean way with minimal custom code.

The following is an example solution with the associated code and tests.

The problem is this: I want a parent tag which will add different class names to the children depending on their order. For this example, we will be adding even/odd and a first/last class to the appropriate children. For the purposes of this example, we will ignore CSS 3.0 Selectors, which could do this entirely in the stylesheet.

First, let's generate our tag lib through grails.

grails create-tag-lib ParentChild

Now that we have our empty files, we can write a test to define the expected behavior in the generated test file. Make sure that you are extending grails.test.GroovyPagesTestCase.

 
class ParentChildTagLibTests extends grails.test.GroovyPagesTestCase {
 
    void testParentContainerShouldRenderChildren() {
        def writer = new StringWriter()
        def markup = new groovy.xml.MarkupBuilder(writer)
        markup {
            ul('class':'myParent') {
                li('class':'myChild even first') {
                    a(href:'/first', 'one')
                }
                li('class':'myChild odd') {
                    a(href:'/second', 'two')
                }
                li('class':'myChild even last') {
                    a(href:'/third', 'three')
                }
            }
        }
 
        assertOutputEquals(
            writer.toString(),
            '<example:parentContainer><example:child label="one" url="[controller: \'first\']"/><example:child label="two" url="[controller: \'second\']"/><example:child label="three" url="[controller: \'third\']"/></example:parentContainer>'
        )
    }
}
 

Now that we have a failing test, we can write the actual code. Notice that I used a custom namespace here (just for the sake of example).

 
class ParentChildTagLib {
 
    static namespace = 'example'
 
    def parentContainer = {attrs, body ->
          // we'll be using this to keep track of our children
          def children = []
          this.pageScope.parentContainerChildren = children
          // this will execute the body, so that the children are
          // appended.  obviously, anything other than child
          // tags will not render correctly.
          body()
 
          def mkp = new groovy.xml.MarkupBuilder(out)
          mkp {
              ul('class': 'myParent') {
                  // iterate over all the children, and
                  // render them here
                  children.eachWithIndex {child, index ->
                      li('class': determineClassNames(child, children, index)) {
                          a(href: g.createLink(child.url), child.label)
                      }
                  }
              }
          }
      }
 
      private def determineClassNames(child, children, index) {
          def classNames = 'myChild'
 
          if (index % 2 == 0) {
              classNames += ' even'
          } else {
              classNames += ' odd'
          }
 
          if (child == children.first()) {
              classNames += ' first'
          } else if (child == children.last()) {
              classNames += ' last'
          }
 
          return classNames
      }
 
      def child = {attrs, body ->
          // simply keep track of the tag attributes
          this.pageScope.parentContainerChildren << attrs
      }
}
 

Now, given the following GSP code:

 
<example:parentContainer>
    <example:child label="one" url="[controller: 'first']"/>
    <example:child label="two" url="[controller: 'second']"/>
    <example:child label="three" url="[controller: 'third']"/>
</example:parentContainer>
 

I get the following HTML generated:

 
<ul class='myParent'>
<li class='myChild even first'>
    <a href='/first'>one</a>
  </li>
<li class='myChild odd'>
    <a href='/second'>two</a>
  </li>
<li class='myChild even last'>
    <a href='/third'>three</a>
  </li>
</ul>
 

There are some limitations to this strategy. First, any other markup outside of the child tag inside the parent is not going to render how you'd expect. Also, nested parent tags aren't going to work for this implementation unless you make some changes. Finally, this implementation won't support body markup in the child tags; however, given a little tweaking it could be made to do that (I'll leave that as an exercise for the reader).

Despite these limitations, if you are looking to do something like breadcrumbs, tab navigation, or custom list rendering, then this may fit your needs.

Related posts:

  1. Grails and JSONP: How Easy is That?
  2. Grails and Google App Engine: Birthing Pains
  3. Grails: Delegating to GORM Persistence in Java
  4. Making Vaadin, PureMVC and Grails Work Together
  5. Getting CloudTools to Work with Grails 1.1.1

Topics: , ,

Leave a comment

Powered by WP Hashcash

Launch: Pathfinder Newsletter

    Get a monthly update on best practices for delivering successful software.

    Subscribe via email


    Subscribe via RSS      RSS icon

Topics

Search

WordPress

Comments about this site: info@pathf.com