Lift 2.2-M1 introduced a new mechanism for transforming XHTML: CSS Selector Transforms (CssBindFunc).  The new mechanism provides a subset of CSS selectors that can be used to transform NodeSeq => NodeSeq.  Examples of this feature include:
  • "#name" #> userName // replace the element with the id name with the variable userName
  • "#chat_lines *" #> listOfChats // replace the content of chat_lines with each element of listOfChats
  • ".pretty *" #> <b>Unicorn</b> // each element with CSS class pretty, replace content with <b>Unicorn</b>
  • "dog=cat [href]" #> "http://dogscape.com" // set the href attribute of all elements with the dog attribute set to cat
  • "#name" #> userName & "#age" #> userAge // set name to userName and age to userAge
CSS Selector Transforms extends NodeSeq => NodeSeq... they are quite literally functions and can be passes as a parameter to anything expecting NodeSeq => NodeSeq or returned as a result for any method that returns NodeSeq => NodeSeq.

Let's look at each of the pieces to see how they work.

First, you must import net.liftweb.util._ and import Helpers._  These packages include the classes and the implicit conversions that make the CSS Selector tranforms work.

The transform is defined by: String representing selector #> transform value.

The selector is a String constant which implements the following subset of CSS Selectors:

  • #id -- selects the element with the specified id
  • .class -- selects all elements have a class attribute where one of the space-separated values equals class
  • attr_name=attr_value -- selects all elements where the given attribute equals the given value
You can replacement rules after the selector:
  • none (e.g., "#id") replaces all matching elements with the values
    "#name" #> "David" // <span><span id="name"/></span> -> <span>David</span>
  • * (e.g., "#id *") replaces the content children of the matching elements with the values
    "#name" #> "David" // <span><span id="name"/></span> -> <span><span id="name>David</span></span>
  • [attr] (e.g., "#id [href]") replaces the matching attribute's value with the values (note in the case of the class attribute, the values are appended to the element's class attributes).
    "#link [href]" #> "http://dogscape.com" // Dogscape -> Dogscape
The right hand side of the CSS Selector Transform can be one of the following:
  • String -- a String constant, for example:
    "#name *" #> "David" // <span id="name"/> -> <span id="name">David</span>
    "#name *" #> getUserNameAsString
  • NodeSeq -- a NodeSeq constant, for example:
    "#name *" #> <i>David</i> // <span id="name"/> -> <span id="name"><i>David</i></span>
    "#name *" #> getUserNameAsHtml
  • NodeSeq => NodeSeq -- a function that transforms the node (yes, it can be a CssBindFunc):
    "#name" #> (n: NodeSeq) => n % ("class" -> "dog") // <span id="name"/> -> <span id="name" class="dog"/>
  • Bindable -- something that implements the Bindable trait (e.g., MappedField and Record.Field)
  • StringPromotable -- A constant that can be promoted to a String (Int, Symbol, Long or Boolean).  There is an automatic (implicit) conversion from Int, Symbol, Long or Boolean to StringPromotable.
    "#id_like_cats" #> true
    "#number_of_cats" #> 2
  • IterableConst -- A Box, Seq, or Option of String, NodeSeq, or Bindable.  Implicit conversions automatically promote the likes of Box[String], List[String], List[NodeSeq], etc. to IterableConst.
    "#id" #> (Empty: Box[String]) // <span><span id="id">Hi</span></span> -> <span/>
    "#id" #> List("a", "b", "c") // <span><span id="id"/></span> -> <span>abc</span>
    "#id [href]" #> (None: Option[String]) <a id="id" href="dog"/> -> <a id="id"/>

    Note that if you bind to the children of a selected element, multiple copies of the element result from bind to an IterableConst (if the element has an id attribute, the id attribute will be stripped after the first element):
    "#line *" #> List("a", "b", "c") // <li id="line>sample</li> -> <li id="line">a</li><li>b</li><li>c</li>
    "#age *" #> (None: Option[NodeSeq]) // <span><span id="age">Dunno</span></span> -> <span/>

    The above use cases may seem a little strange (they are not quite orthogonal), but they address common use cases in Lift.

  • IterableFunc -- A Box, Seq, or Option of functions that transform NodeSeq => String, NodeSeq, Seq[String], Seq[NodeSeq], Box[String], Box[NodeSeq], Option[String] or Option[NodeSeq].  The same rules for handling multiple values in IterableConst apply to IterableFunc.  Implicit conversions automatically promote the functions with the appropriate signature to an IterableFunc.
You can chain CSS Selector Transforms with the & method:
"#id" #> "33" & "#name" #> "David" & "#chat_line" #> List("a", "b", "c") & ClearClearable

CSS Selector Transforms offer an alternative to Lift's traditional binding (See Helpers.bind()).