Did you ever want to use builders in your grails taglib and wondered why the approach mentioned in the grails documentation doesn’t work at all? Here is what I found out after a lot of trial and error and digging in the MarkupBuilder, BuilderSupport and GroovyPagTagBody sources. Let’s stick with the example from the documentation and correct it until it works (the impatient can scroll down):
-
def dialog = { attrs, body ->
-
markup {
-
div(‘class’: ‘dialog’) {
-
body()
-
} } }
The first small remark is about line 3: markup{//doSomething} is similiar to markup.call({//doSomething}). BuilderSupport intercepts all method calls and MarkupBuilder produces xml tags from these method calls. So this will lead to an enclosing <call> tag. Surely not what we want.
-
def dialog = { attrs, body ->
-
markup.div(‘class’: ‘dialog’) {
-
body()
-
} }
Now let’s have a closer look at the body parameter. This is a GroovyPageTagBody, a special Closure which captures output using a special Writer (GroovyPageTageWriter). These bodies are usually called by various GroovyPage.invokeTag methods (see another following article on how to investigate groovy pages rendering) and return either a StreamCharBuffer (a special Writable and CharSequence) or the result of the closure evaluation, which is also Writable or a CharSequence if we are lucky.
So what is the right way to pass a CharSequence to the builder if we want our MarkupBuilder to use it as tag content? This will not work:
The String will be evaluated, but as it’s not in a method call, our MarkupBuilder doesn’t know what to do with it. There are two possibilities to do it right:
-
markup.div(‘class’: ‘dialog’, "some text")
-
markup.div(‘class’: ‘dialog’) {
-
mkp.yield "some text"
-
}
If we now replace “some text” with our GroovyPageTagBody call, we are nearly done. But wait, there’s one more thing. mkp (short for markup.getMkp()) returns a MarkupBuilderHelper (since Groovy 1.6.6, prior it was the builder itself). Instead of mkp.yield("some text") we could also use the yield method of the MarkupBuilder itself: yield("some text", true). But, hey, what means true? It’s the XML escaping of the text, which is also done if we pass the text as last parameter to the builder method (as in line 2 above). If we expect our body to contain markup, we should omit escaping. Of course there is also a helper method for that and our corrected example now finally looks right:
-
def dialog = { attrs, body ->
-
markup.div(‘class’: ‘dialog’) {
-
mkp.yieldUnescaped body()
-
} }
One last remark: Prior to Groovy 1.7.2 MarkupBuilderHelpers yield and yieldUnescaped methods were duplicated in MarkupBuilder itself, so the mkp prefix could be omitted. This is no longer true. You have to either fall back to the ugly boolean parameter or use the helpers pretty named methods.




u did a good job…coool.