Platinum Partner
css,frameworks,scala,lift

Recursive Snippet Processing with Lift

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.

Published at DZone with permission of {{ articles[0].authors[0].realName }}, DZone MVB. (source)

Opinions expressed by DZone contributors are their own.

{{ tag }}, {{tag}},

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}
{{ parent.authors[0].realName || parent.author}}

{{ parent.authors[0].tagline || parent.tagline }}

{{ parent.views }} ViewsClicks
Tweet

{{parent.nComments}}