7.10 CSS Selector Transforms
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
-
"li *" #> userName & "li [class]" #> "foo" // set the contents of all <li> element with username and class to foo
-
"li *" #> userName & "li [class+]" #> "foo" // set the contents of all <li> element with username and append foo to the class
-
"*" #> <span>{userName}</span> // set all the elements to <span>{userName}</span>
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
-
element_name - selects all the elements matching the name
-
* - selects all elements
-
@name - selects all elements with the specified name
-
:button - selects all the elements with type="button"
-
:checkbox - selects all the elements with type="checkbox"
-
:file - selects all the elements with type="file"
-
:password - selects all the elements with type="password"
-
:radio - selects all the elements with type="radio"
-
:reset - selects all the elements with type="reset"
-
:submit - selects all the elements with type="submit"
-
:text - selects all the elements with type="text"
You can put 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>
-
*+ or *< (e.g., "#id *+") appends the value to the the content children nodes
"#name *+" #> "David" // <span><span id="name">Name: </span></span> -> <span><span id="name>Name: David</span></span>
-
-* or >* (e.g., "#id -*") prepends the value to the the content children nodes
"#name -*" #> "David" // <span><span id="name"> Pollak</span></span> -> <span><span id="name>David Pollak</span></span>
-
[attr] (e.g., "#id [href]") replaces the matching attribute’s value with the values.
"#link [href]" #> "http://dogscape.com"
// <a href="#" id="link">Dogscape</a> -> <a href="http://dogscape.com" id="link">Dogscape</a>
-
[attr+] (e.g., "#id [class+]") appends the value to the existing attribute.
"span [class+]" #> "error"
// <span class"foo">Dogscape</span> -> <span class"foo error">Dogscape</span>
-
[attr!] (e.g., "#id [class!]") removes the matching value to the existing from.
"span [class!]" #> "error"
// <span class"error foo">Dogscape</span> -> <span class"foo">Dogscape</span>
-
^^ - lifts the selected element to the root of the elements that are returned making it possible to choose an element from a template
-
^* - lifts the selected element’s children to the root of the elements that are returned making it possible to choose an element’s children from a template
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 NodeSeq => NodeSeq, 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()).
(C) 2012 David Pollak