Seven Things


One of Lift's key features in Comet (or server push) support and associated Ajax support. This page contains a multi-user chat application with Comet (server-push) that updates the browser when anyone sends a chat message. There's an input box that allows the user to send a line into the chat and that's done via an asynchronous call to the server (the page is not reloaded as part of the call).

Let's take a look at the markup for the Comet (server pushed list of chat items) part of the application:
Listing: /comet.html
        <ul class="lift:comet?type=Chat">
          <li>Line 1</li>
          <li class="clearable">Line 2</li>
          <li class="clearable">Line 3</li>
        </ul>
        
We simply define the markup and mark the <ul> tag with a snippet invocation that loads the Chat Comet component. Let's look at the Chat component.
Listing: /net/liftweb/seventhings/comet/Chat.scala
/**
 * The comet chat component
 */
class Chat extends CometActor with CometListener {
  private var msgs: Vector[String] = Vector() // private state

  // register this component
  def registerWith = ChatServer

  // listen for messages
  override def lowPriority = {
    case v: Vector[String] => msgs = v; reRender()
  }

  // render the component
  def render = ClearClearable & "li *" #> msgs
}

Once again, something that's hard or impossible in other web frameworks is is trivial in Lift. The developer doesn't worry about the plumbing of how the browser polls for changes. By default Lift uses long polling and multiplexes multiple comet components into a single long poll, but when web sockets are standardized, Lift will automatically support them without requiring any code changes. The developer focuses on the semantics of "when this changes on the server, update the component" and Lift takes care of the rest.

Note that you can attempt to attack the site with a cross site scripting attack (e.g., type <script>alert('I ownz your machine')</script> into the chat box). You'll see that Lift properly escapes the input without any developer intervention.

Let's take a look at the markup for the Ajax input form:
Listing: /comet.html
        <form class="lift:Form.ajax">
          <input class="lift:ChatIn" id="chat_in">
          <input type="submit" value="Chat">
        </form>
We designate the form as Ajax with the Form.ajax snippet. The text input invokes the ChatIn snippet. Let's look at that snippet:
Listing: /net/liftweb/seventhings/snippet/ChatIn.scala
/**
 * Handle input by sending the input line
 * to the ChatServer
 */
object ChatIn {
  // max count per session
  private object lineCnt extends SessionVar(0)

  def render =
    "*" #> SHtml.onSubmit(s => {
      if (s.length < 50 && lineCnt < 20) { // 20 lines per session
        ChatServer ! s // send the message
        lineCnt.set(lineCnt.is + 1)
      }
      SetValById("chat_in", "") // clear the input box
    })
}
Once again, we didn't need to do any plumbing. Lift takes care of associating the behavior (sending the input String to the ChatServer.) We didn't set up routes or anything else. Further, this code is resistant to replay attacks because the automatically generated routes are dynamic and random and cannot be predicted. And in case you're worried about doing things via standard REST calls, Lift has excellent REST support. And Lift supports Comet for data as well as HTML, so you can use Lift to serve your Cappuccino, SproutCore, ExtJS, etc. apps and get full Comet support.

Lift is Copyright 2007-2011 WorldWide Conferencing, LLC. Distributed under an Apache 2.0 License.