Version 2, last updated by hat at November 16, 2010 14:17 UTC
Radio Lists And Drop Downs From Enumerations
The original content can be found on this post from the Lift google group
This “How To” will provide step by step instructions on creating radio button lists and drop down menus from a Scala Enumeration object.
Let’s say you have the following Enumeration for your website.
object ExchangeMethod extends Enumeration {
val Pickup = Value("Pickup")
val Ship = Value("Ship")
val PickupOrShip = Value("Pickup or Ship")
}Now you want to represent each ExchangeMethod.Value as a radio button or drop down item on a web form, and have the code allow for future additions to your Enumeration. Let’s say you wanted to add another ExchangeMethod.Value called “Online Transfer”; obviously you would want your site to account for this in the radio button list or drop down menu without having to explicitly add another radio button or list item.
The Basic Common Code
1. Your form needs to include a “prefix:element” bind tag to replace with the radio button or drop down XHTML. It’s up to you what to choose as your “prefix” and “element”, just make sure you use the right bind code later on. Here’s an example of what the XHTML would look like with “item:exchangeMethod” and “item:submit” as the “prefix:element” tags.
<lift:SnippetName.doSomething form="post" multipart="false"> ... <item:exchangeMethod /> <item:submit/> ... </lift:SnippetName.doSomething>
2. Your most basic lift snippet will look something like this.
package com.yourpackage
import net.liftweb.http._
import S._
import net.liftweb.util._
import Helpers._
import scala.xml._
class SnippetName {
def doSomething (xhtml : NodeSeq) : NodeSeq = {
def processForm () : Unit = {...} //some form manipulation stuff
var chosenMethod: Box[ExchangeMethod.Value] = Empty
bind("item", xhtml,
"exchangeMethod" -> ... /* stuff to replace the prefix:element tag */
"submit" -> SHtml.submit( "Submit Form" /*button name*/,
processForm /*function to call*/)
)
}
}Here’s a quick overview:
- When your snippet is called, all the XHTML within the “lift:SnippetName.doSomething” tags will be passed to your “doSomething” method as the parameter “xhtml”.
- “var chosenMethod” is a Box (see “Notes on Boxes”1) that contains an ExchangeMethod.Value.
- bind is looking for an “item” prefix within the xhtml, binding stuff to “item:exchangeMethod”, and also binding a submit button to the processForm function. The “item:exchangeMethod” bind is where we are going to be making our select drop down or radio group.
- Side note: The SHtml.submit parameter has no parentheses, while the method declaration does. This means that you are using the function as a parameter, not evaluating the function and using that (more info on this post on the google group).
Making a select drop down
Here’s the excerpt from the above snippet, this time creating a drop down.
class SnippetName {
def doSomething (xhtml : NodeSeq) : NodeSeq = {
def processForm () : Unit = {...} //some form manipulation stuff
var chosenMethod: Box[ExchangeMethod.Value] = Empty
bind("item", xhtml,
"exchangeMethod" -> SHtml.selectObj[ExchangeMethod.Value](
ExchangeMethod.values.toList.map(v => (v,v.toString)),
Empty,
selected => chosenMethod = Full(selected) ),
...
)
}
}Explanation:
- The method signature is
def selectObj[T](
options : Seq[(T, String)],
default : Box[T],
onSubmit : (T) => Unit,
attrs : (String, String)* )- Don’t forget to include the type “T” that this SelectObj is for, otherwise the compiler will get confused and crash. In this case, the type “T” is “ExchangeMethod.Value”.
- “options” is a sequence of tuples (theValue, string representation). To satisfy this, we call ExchangeMethod.values.toList, which gives us a List[ExchangeMethod.Value]. From there, we call the List class’s “map” method to make a list of tuples.
- “default” is the preselected form value. We’re using Empty (the EmptyBox singleton, see “Notes on Boxes”1)
- “onSubmit” is the function that’s executed on form submission. In this case, we are assigning a Full Box filled with the “selected” variable to “var chosenMethod”.
Making a radio button list
Here’s the excerpt from the above snippet, this time creating a group of radio buttons.
class SnippetName {
def doSomething (xhtml : NodeSeq) : NodeSeq = {
def processForm () : Unit = {...} //some form manipulation stuff
var chosenMethod: Box[ExchangeMethod.Value] = Empty
val radios =
SHtml.radio( ExchangeMethod.values.toList.map(_.toString), Empty,
selected =>
chosenMethod = Box(ExchangeMethod.withName(selected)) )
bind("item", xhtml,
"exchangeMethod" -> radios.toForm,
...
)
}
}Explanation:
- The XHTML should be pretty self explanatory, as it’s the same as all other bind tags.
- In the snippet, I made a “val radios” that uses the SHtml.radio method with signature:
SHtml.radio( radioChoices: Seq[String], defaultVal: Box[String], submitFunction: (String) => Any, attributes: (String, String)* )
- “radioChoices” are the buttons that will be displayed. ExchangeMethod.values.toList returns List[ExchangeMethod.Value]; use the map method on lists to convert it to List[String], which matches the SHtml.radio method signature.
- “defaultVal” is the default value for the radio choices, so use Empty, which is the EmptyBox singleton (see “Notes on Boxes”1).
- The function executed on submit is using Box’s apply(in: Option[T]) “constructor” to yield a new Box from an Option, since ExchangeMethod.withName(selected: String) returns an Option.
- Calling radio.toForm XHTML-izes the radio buttons for you. “val radio” is actually a ChoiceHolder (more info here). The actual structure of “val radio” is as follows:
ChoiceHolder( List( ChoiceItem(Pickup, ...XHTML...), ChoiceItem(Ship, ...XHTML...), ChoiceItem(Pickup or Ship, ...XHTML...) ) )
1 Notes on Boxes
Boxes are like Scala’s Option class with extra goodies, so basically it’s just a container for things so you don’t get Null Pointer Exceptions all over the place. Use Full(object) to make a Box that contains something, or Empty if you want an empty Box. As of Lift 1.1, the Box class and object have been moved to net.liftweb.common from net.liftweb.util, so just be aware if you’re using Lift 1.0 or Lift 1.1. However, for my purposes I was using this explanation of Box in Lift 1.0.