Up: Chapter 4

4.8 Ajax

In addition to full-page HTML, Lift support Ajax forms. Because Lift’s forms are functions on the server-side associated with GUIDs in the browser, switching a form from full page load to Ajax is, well, pretty trivial. Let’s look at the markup:
ajax.html
<div id="main" class="lift:surround?with=default&at=content">
  <div>
    An example of doing forms with Ajax.
  </div>
  
  <form class="lift:form.ajax">
    <div class="lift:AjaxExample">
      Name: <input name="name"><br>
      Age: <span class="lift:Msg?id=age&errorClass=error">error</span><input name="age" id="the_age" value="0"><br>
      <input type="submit" value="Submit">
    </div>
  </form>
</div>
​
The key difference is: <form class="lift:form.ajax">. This invokes Lift’s built-in form snippet and designates the current form as an Ajax form. Then the snippet does the following:
AjaxExample.scala
package code
package snippet
​
import net.liftweb._
import http._
import common._
import util.Helpers._
import js._
import JsCmds._
import JE._
import scala.xml.NodeSeq
​
/**
 * Ajax for processing... it looks a lot like the Stateful example
 */
object AjaxExample {
  def render = {
    // state
    var name = ""
    var age = "0"
    val whence = S.referer openOr "/"
​
    // our process method returns a
    // JsCmd which will be sent back to the browser
    // as part of the response
    def process(): JsCmd= {
​
      // sleep for 400 millis to allow the user to
      // see the spinning icon
      Thread.sleep(400)
      
      // do the matching
      asInt(age) match {
        // display an error and otherwise do nothing
        case Full(a) if a < 13 => S.error("age", "Too young!"); Noop
​
        // redirect to the page that the user came from
        // and display notices on that page
        case Full(a) => {
          RedirectTo(whence, () => {
            S.notice("Name: "+name)
            S.notice("Age: "+a)
          })
        }
        
        // more errors
        case _ => S.error("age", "Age doesn't parse as a number"); Noop
      }
    }
​
    // binding looks normal
    "name=name" #> SHtml.text(name, name = _, "id" -> "the_name") &
    "name=age" #> (SHtml.text(age, age = _) ++ SHtml.hidden(process))
  }
}
The code looks a lot like the Stateful code. Except that we don’t bind to the submit button (the submit button is not serialized over Ajax), so we have to add a hidden field to the age field which does the processing.
The process() method returns a JsCmd which is the JavaScript command to send back to the browser in response to the Ajax form submission. In this case, we’re either using S.error to display error notices followed by a Noop or we’re doing a redirect.
We pause for 400 milliseconds in the process() method so that the user can see the spinner in the browser indicating that an Ajax operation is taking place.
But the core take-away is that normal HTML processing and Ajax processing are almost identical and both are super-easy.
Up: Chapter 4

(C) 2012 David Pollak