Monday, April 03, 2006

Grails: Tag Libraries & The Power of Closures

When we started developing Grails we wanted to support a dynamic view technology that allowed scriptlets in Groovy instead of Java, but without falling into the trap of having scriptlets intermingled with HTML code a trap that many Java developers have spent years trying to avoid.

Historically though JSP custom tags have always been a bit of a pain to write, they're designed to cover every variation of tag under the sun, hence the API is complicated and the tag lib descriptors verbose. Grails is all about simplicity, so how do we avoid introducing this level of complexity into our Grails applications. The answer came in the form of anonymous code blocks or closures.

I would say 90% of the tags that are written for JSP out there fall into one of three categories: simple, logical or iterative. There are those more complex tags that have relationships to each other via nesting, but the vast majority are of the aforementioned type and Grails is about making the most common cases easy, but still allowing the flexibility to scale to the more complex (thats why we still support JSP).

So what have closures done to make custom tags easier? Well Grails allows you to define a custom tag as a JavaBean property. No descriptors, no configuration, and everything is reloaded at runtime so no need to restart that application server. So lets look at an example from the tag library that ships with Grails (simplified for clarity):


class ValidationTagLib {
@Property eachError = { attrs, body ->
def errors = attrs.bean.errors
if(errors) {
errors.each { body(it) }
}
}
}


Each tag library is simply a class that ends with the convention "TagLib". The above example contains an "eachError" tag that loops through each error contained within the "bean" attribute and invokes the "body" of the tag. Note how the body of the tag itself is a closure and hence callable, the attributes are a map. To use this tag we simple call it from our GSP no need to import the tag library or anything, the error itself is available using Groovy's implicit 'it' variable which was passed to the body closure:


<g:eachError bean="${myBean}">
<p style="color:red;">${it.defaultMessage}</p>
</g:eachError >


Now thats pretty neat and simplistic, but this is where using closures for defining tags becomes really powerful. How about we want to re-use our "eachError" tag else where? Say we want to implement a default rendering tag called "renderErrors" that renders our errors as an HTML list. Well we can re-use the "eachErrors" tag to accomplish this:


@Property renderErrors = { attrs, body ->
def markup = new groovy.xml.MarkupBuilder(out)
markup.ul() {
eachError(attrs) { err ->
li( message(code:err.code) )
}
}
}


The above code creates a new MarkupBuilder instance which is used to generate an HTML list re-using the eachError tag defined previously, it actually uses a third Grails tag called "message" to retrieve the message for the error code. To call the "renderErrors" tag we simply do:


<g:renderErrors bean="${myBean}"/>


Grails users can of course customise the inbuilt tags and add brand new ones simply by adding new tag library classes in the "grails-app/taglib" directory. So there you have it, custom tags have never been easier, and your markup can remain scriptlet free without the need to invest huge amounts of time creating a custom JSP tag library.

8 comments:

Jorge Baroudi said...

Excellent, got to try our example, i don't use custom tags due to the facts you have point out, and this seems to be very friendly. I like it very much.

I'm going to a local conference of Ruby in town and want it to check upon java alikes, grails project i believe it will get further :)

Keep on the good work!

Regards

Ayisha said...

nice site for more books I have some more gifts..



Reference books

Books

Kitaben

books

Reference Books

tutorials

rapidshare tutorials

rapidshare books

MZWorld

Upload Books

MZWorld Library

Books Forum

Anonymous said...

Can I somehow access to the nodes in custom tag body? for example how can I enumerate "column" nodes in class groovy code?

<g:myGrid name="mygrid">
<columns>
<column name="MyId" caption="MyColCaption1" type="int"/>
<column name="MyDate" caption="MyColCaption2" type="date"/>
</columns>
</g:myGrid>

Anonymous said...

can i use body() in MarkupBuilder instance? like this:
@Property dialog = { attrs, body ->
def markup = new groovy.xml.MarkupBuilder(out)
markup.div() {
body()
}

derfefww said...

It is the shadow of legend Gold which make me very happy these days, my brother says sol gold is his favorite games gold he likes, he usually buy some buy shadow of legend Gold to start his game and most of the time he will win the cheap shadow of legend Gold back and give me some shadow of legend money to play the game.

Anonymous said...

網頁設計,情趣用品店,情趣用品專賣網

A片,色情A片,免費A片,成人影片,色情影片,a片免費看,情色貼圖,情色文學,情色小說,色情小說
AV,AV女優

辣妹視訊,美女視訊,視訊交友網,視訊聊天室,視訊交友,視訊美女,免費視訊,免費視訊聊天,視訊交友90739,免費視訊聊天室,成人聊天室,視訊聊天,視訊交友aooyy
哈啦聊天室,辣妺視訊,A片,色情A片,視訊,080視訊聊天室,視訊美女34c,視訊情人高雄網,視訊交友高雄網,0204貼圖區,sex520免費影片,情色貼圖,視訊ukiss

Anonymous said...

I want to tell you, I know a website sell the gaia gold, and in this website all gaia online gold is very cheap, there had many customer, if you want to buy gaia gold, I suggest you to come here then to buy the cheap gaia gold.

Anonymous said...

^^ nice blog!! ^@^

徵信, 徵信網, 徵信社, 徵信社, 感情挽回, 婚姻挽回, 挽回婚姻, 挽回感情, 徵信, 徵信社, 徵信, 徵信, 捉姦, 徵信公司, 通姦, 通姦罪, 抓姦, 抓猴, 捉猴, 捉姦, 監聽, 調查跟蹤, 反跟蹤, 外遇問題, 徵信, 捉姦, 女人徵信, 女子徵信, 外遇問題, 女子徵信, 外遇, 徵信公司, 徵信網, 外遇蒐證, 抓姦, 抓猴, 捉猴, 調查跟蹤, 反跟蹤, 感情挽回, 挽回感情, 婚姻挽回, 挽回婚姻, 外遇沖開, 抓姦, 女子徵信, 外遇蒐證, 外遇, 通姦, 通姦罪, 贍養費, 徵信, 徵信社, 抓姦, 徵信, 徵信公司, 徵信社, 徵信公司, 徵信社, 徵信公司, 女人徵信,

徵信, 徵信網, 徵信社, 徵信網, 外遇, 徵信, 徵信社, 抓姦, 徵信, 女人徵信, 徵信社, 女人徵信社, 外遇, 抓姦, 徵信公司, 徵信社, 徵信社, 徵信社, 徵信社, 徵信社, 女人徵信社, 徵信社, 徵信, 徵信社, 徵信, 女子徵信社, 女子徵信社, 女子徵信社, 女子徵信社, 徵信, 徵信社, 徵信, 徵信社, 徵信社,

徵信, 徵信社,徵信, 徵信社, 徵信, 徵信社, 徵信, 徵信社, 徵信, 徵信社, 徵信, 徵信社, 徵信, 徵信社, 徵信, 徵信社, 徵信, 徵信社, 徵信, 徵信社, 徵信, 徵信社, 徵信, 徵信社, 徵信, 徵信社, 徵信, 徵信社, 徵信, 徵信社, 外遇, 抓姦, 離婚, 外遇,離婚,

徵信社,徵信, 徵信社, 徵信, 徵信社, 徵信,徵信社, 徵信社, 徵信, 外遇, 抓姦, 徵信, 徵信社, 徵信, 徵信社, 徵信, 徵信社, 徵信社, 徵信社, 徵信社,徵信,徵信, 徵信, 外遇, 抓姦