Eric is a DZone MVB and is not an employee of DZone and has posted 17 posts at DZone. You can read more from them at their website. View Full User Profile

Recursive Snippet Processing with Lift

07.23.2010
| 5434 views |
  • submit to reddit
I really like how Lift's 'template' engine works.  In short, you define XML tags that map to a Class and Method for execution.  For instance, a basic HTML template looks like:
<lift:MyClass.myMethod>
<div>Hello, <my:name/>. Welcome to my sample web app</div>
</lift:MyClass.myMethod>
This will result in the myMethod function on MyClass being called, which can then easily replace <my:name/> with a dynamic value.

The real power comes from the fact that the Lift framework will continue to (re)process the XML until all Lift tags have been resolved.  This means that a call to one snippet can produce a call to one or more snippets.

I came across an example of this on a recent project.  I wanted to produce the same HTML block for multiple snippets.  My first effort at refactoring produced something similar to this:
<lift:MyClass:showAttr1 eager_eval="true" name="Attribute 1">
<lift:embed what="attribute" />
</lift:MyClass>
My MyClass looked like:
class MyClass extends AttributeHelper {

private val attributeDefinitnion = ...
val name = S.attr("name").openOr("Unnamed Attribute")

def showattr1(xml:NodeSeq) : NodeSeq = {
attrHelperBind(attributeDefinition, name, xml)
}
}
The AttributeHelper trait defined the attrHelperBind method which took the attributeDefinition and used the bind method to replace the XML tags defined in the attribute template that was embedded in the body.  Note, I needed the eager_eval="true" attribute so that the embed tag would be executed before the showAttr1 tag.

This worked well and greatly reduced the amount of boiler plate code needed for each attribute.  However, since Lift will continue to evaluate the XML until all the tags are processed, I realized I could further improve it.  I created a generic snippet that simply returned the following block:
<lift:MyClass.myMethod eager_eval="true">
<lift:embed what="attribute" />
</lift:MyClass.myMethod>
This allowed me to have a very generic entry in my HTML:
<lift:Myhelper.helper snippet="MyClass.myMethod"/>
The implementation of this Snippet is:
def helper(xml:NodeSeq) : NodeSeq = {
val snippet = S.attr("snippet").openOr("Helper.default")
new Elem("lift", snippet, Attribute("eager_eval", Text("true"), Null), TopScope,
<lift:embed what="attribute" />)
}
This simply produces the original XML block, which will then be processed normally. The Elem call produced a element named <lift:{snippet}> with the body
<lift:embed what="attribute" />. You must use the Elem object to create the XML because you cannot have dynamic tag names in XML literals.  IE:
def myXml(name:String) = {
<lift:{name}>Body</lift:{name}>
}
is not legal as the XML literal will not be parsed correctly.

This ability to 'recursively' process the Lift XML tags enables the development of easy helper methods to allow the final XHTML templates to be very concise and readable.
References
Published at DZone with permission of Eric Daugherty, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)