Endless scrolling with Lift

For the upcoming Audiotool 2.0 launch in collaboration with Burn I am developing our new website. The framework choice was quite obvious for me since I love Scala and really like Lift.

Lift is very nice because of its excellent Ajax and Comet support. My main problem getting started with Lift was the lack of documentation and real world examples. E.g. if you want a custom 404 page without a redirect your only source of information is a thread in the mailing list. Lift also comes with its own terminology and a lot of custom DSLs.

Cricitcs aside: I am absolutely happy that Lift exists. You simply have to start using it. The documentation has also improved a lot. Simply Lift and Exploring Lift are available for free and the Wiki contains a lot of useful information too.

I want to give you a simple example of why I think that Lift is simply awesome and what sold me to the framework. You probably know those endless scrolling pages.

Let’s see how to develop something similar with Lift and Scala. First of all we have to prepare a little template.

<div class="lift:surround?with=default;at=content">
  <div class="lift:endless">
    <ul>
      <li>item0</li>
      <li>item1</li>
      <li>item2</li>
    </ul>
  </div>
</div>

That is all the HTML we need. In fact we need even less but if you are going for designer friendly templates you could extend it with your usual page chrome.

There are two calls to Scala code in this template. lift:surround will trigger the SurroundSnippet and insert the current template into the default template at the content position.

Our duty is now the implementation of lift:endless. Lift comes with a dynamic convention-over-configuration lookup for the so-called snippets. I am not a fan of COC so I will stick to the manual mechanism of binding endless to a snippet. Lift offers the DispatchSnippet in this case.

You will have to add this line to Boot.scala:

LiftRules.snippetDispatch append Map("endless" -> EndlessSnippet)

It tells Lift to invoke the dispatch method of the EndlessSnippet object whenever a snippet named endless is encountered.

So the only missing piece is the implementationof the EndlessSnippet and this is where Lift really shines.

import net.liftweb.common._
import net.liftweb.util._
import net.liftweb.http._
import net.liftweb.util.Helpers._
import net.liftweb.http.js.jquery.JqJsCmds
import net.liftweb.http.js.JsCmds
import net.liftweb.http.js.JE
import scala.xml.{NodeSeq, Unparsed}

object EndlessSnippet extends DispatchSnippet with Loggable {
  def dispatch = {
    // If you want to execute render only when "embed.xyz" is called
    // you would match on "xyz" here.
    case _ => render
  }

  def render =  {
    // Generate a random identifier
    val containerId = nextFuncName

    // The page which we are currently looking at.
    var page = 0

    // Method to be called by JavaScript.
    def process(in: Any) = in match {
      case id: String if id == containerId =>
        page += 1
        logger.debug("Generating page "+page+".")

        // Generate new markup and append it to the list.
        JqJsCmds.AppendHtml(id, getItems(page))
      case _ =>
        logger.error("Illegal data has been sent.")
        JsCmds.Noop
    }

    // Here we marry the template with our code.
    // "*" is a CSS selector that matches anything and #> binds it to the content
    // we specify.
    // The most interesting part here is the JavaScript  which we bind to the process
    // closure. Lift does all the magic for us and calls the server-side method once the
    // user scrolls to the bottom of the page.
    "*" #> (<ul id={containerId}>{getItems(page)}</ul> ++ JsCmds.Script(JE.JsRaw(
"""$(window).scroll(function(){
  if($(window).scrollTop() == $(document).height() - $(window).height()) {"""+
    SHtml.jsonCall(containerId, process)._2.toJsCmd+"""
  }
})""").cmd))
  }

  def getItems(offset: Int): NodeSeq = {
    val count = 100
    val start = offset * count
    val end = from + count

    // We generate a couple of <li> elements here.
    for(i <- start until end) yield {
      <li>{"item"+i}</li>
    }
  }
}

This little amount of code really shows the power of Lift. Simply write a closure and bind it to your JavaScript. Lift does all the plumbing for you. Then you only have to implement some logic on the server. In most cases you do not even have to write any JavaScript since Lift offers Ajax links, forms etc. already. SHtml.a(...) generates a link which will trigger a function on the server for instance.

But please beware: I am not sure if SHtml.jsonCall is actually the way how you should do this. There might be a better way of calling a server-side method via JavaScript.

There is of course more to Lift. Comet is where things get really cool and the record framework seems interesting as well. Hopefully this little excursion into Scala and Lift could quicken your appetite. Since I am usually not a frontend fan this made it interesting and fun again.

I will try to post a complete project in the next days as well. Before starting with the Audiotool website I wrote some test cases that might help other people getting started.

2 Responses to “Endless scrolling with Lift”


Leave a Reply