Up: Chapter 4

4.4RequestVars

In this example, we’re going to preserve state during the request by placing state in RequestVars (see 7.8 on page 1↓).
Lift has type-safe containers for state called XXXVars. There are SessionVars that have session scope, WizardVars that are scoped to a Wizard and RequestVars that are scoped to the current request [F]  [F] In this case, “request” means full HTML page load and all subsquent Ajax operations on that page. There’s also a TransientRequestVar that has the scope of the current HTTP request.. Vars are defined as singletons: private object name extends RequestVar(""). They are typed (in this case, the type is String) and they have a default value.
So, let’s look at the HTML which looks shockingly like the HTML in the last two examples:
requestvar.html
<div id="main" class="lift:surround?with=default&at=content">
  <div>
    Using RequestVars to store state
  </div>
      
  <div>
    <form class="lift:ReqVar?form=post">
      Name: <input name="name"><br>
      Age: <input name="age" id="the_age" value="0"><br>
      <input type="submit" value="Submit">
    </form>
  </div>
</div>
​
Now, let’s look at the snippet code:
ReqVar.scala
package code
package snippet
​
import net.liftweb._
import http._
import common._
import util.Helpers._
import scala.xml.NodeSeq
​
/**
 * A RequestVar-based snippet
 */
object ReqVar {
  // define RequestVar holders for name, age, and whence
  private object name extends RequestVar("")
  private object age extends RequestVar("0")
  private object whence extends RequestVar(S.referer openOr "/")
​
  def render = {
    // capture the whence... which forces evaluation of
    // the whence RequestVar unless it's already been set
    val w = whence.is
​
    // we don't need an explicit function because RequestVar
    // extends Settable{type=String}, so Lift knows how to
    // get/set the RequestVar for text element creation
    "name=name" #> SHtml.textElem(name) &
    // add a hidden field that sets whence so we
    // know where to go
    "name=age" #> (SHtml.textElem(age) ++ 
                   SHtml.hidden(() => whence.set(w))) &
    "type=submit" #> SHtml.onSubmitUnit(process)
  }
​
  // process the same way as
  // in Stateful
  private def process() =
    asInt(age.is) match {
      case Full(a) if a < 13 => S.error("Too young!")
      case Full(a) => {
        S.notice("Name: "+name)
        S.notice("Age: "+a)
        S.redirectTo(whence)
      }
      
      case _ => S.error("Age doesn't parse as a number")
    }
}
The snippet is a singleton because the state is kept in the RequestVars.
We use SHtml.textElem() to generate the <input> tag. We can pass the RequestVar into the method and the function that gets/sets the RequestVar is generated for us.
The use of this mechanism for doing stateful forms versus the StatefulSnippet mechanism is one of personal choice. Neither one is better, they are just different.
Next, let’s look at how to get more granular with error messages.
Up: Chapter 4

(C) 2012 David Pollak