Monday, February 21, 2011

Grails Taglib : The template composition pattern

Grails leverages Sitemesh, a decorator engine, to support view layouts. Sitemesh layout is restricted to page layout and are not applicable to do template layout. For example a template layout which describes the composition of a table and a toolbar.

The purpose of this taglib is to define a ui composition pattern to leverage template layout.

The tags


The taglib will expose the following tags :
  • ui:composition: The main tag. It takes a template attribute and it has ui:define tags as childrens.
  • ui:define: The children tags. It takes a name attribute and
    a composition attribute which is a trick to create the parent<->children relationship. The body content of each ui:define tags will be used inside the ui:composition template.


The taglib

Writing taglib in Grails is a breeze on the contrary of frameworks like JSF.
class UICompositionTagLib {
static namespace = 'ui'
def out
def composition = { attrs, body ->
if (!attrs.template) {
throwTagError("Tag [composition] is missing required attribute [template]")
}
Composition composition = new Composition()
body(composition)
out << g.render(template: attrs.template, model: composition.defines)
}
def define = { attrs, body ->
if (!attrs.composition) {
throwTagError("Tag [define] is missing required attribute [composition]")
}
if (!attrs.name) {
throwTagError("Tag [define] is missing required attribute [name]")
}
attrs.composition.defines.put(attrs.name, body)
}
}
view raw gistfile1.java hosted with ❤ by GitHub


Composition contains a simple map:
class Composition {
Map defines = new HashMap()
}
view raw gistfile1.java hosted with ❤ by GitHub


Example

The view

<ui:composition template="/table">
<ui:define composition="${it}" name="toolbar">
[Toolbar content]
</ui:define>
<ui:define composition="${it}" name="table">
[Table content]
</ui:define>
</ui:composition>
view raw gistfile1.xml hosted with ❤ by GitHub


The "table" template

<div>
${toolbar()}
</div>
<div>
${table()}
</div>
view raw gistfile1.xml hosted with ❤ by GitHub


Conclusion

Easy!

This taglib can replace Sitemesh layout however Sitemesh provides convention over configuration for layouts and Sitemesh is in Java and therefore much faster than Groovy [1].

UI composition pattern is more generic and leverage template layout and template reusing.


[1] Mean response time for 50 concurrent thread :
benchSitemesh 0.019751053s
benchComposition 0.027618479s

3 comments:

  1. You can annotate defines map in Composition class with @Delegate and you should be able to access it directly from composition:

    out << g.render(template: attrs.template, model: composition)

    attrs.composition.put(attrs.name, body)

    or

    attrs.composition[attrs.name] = body

    anyway this could lead to a little delay in executions, I didn't try it

    ReplyDelete