Version 6, last updated by heralight at 27 Feb 06:11 UTC

Lift Web applies the concepts of functional programming to web page rendering.  Rather than treating an HTTP request as a composition of output, Lift treats an HTTP request as a transformation of input to output.

The first transformation in the HTML rendering pipeline is a transformation of the URL to a template (this is also known as view first).  In Scala notation, this is Req => NodeSeq (Req being the request and NodeSeq representing a sequence of XHTML or HTML5 nodes).

The template is then passed to LiftSession.processTemplate method which applies the following transformation:
  • If the first tag in the file is an <html> tag and it contains one of the following attributes lift:content_id or l:content_id,  or if the <body> tag contains lift:content_id=xxx in the class attribute, then node where the processing starts is the one with the matching id, otherwise the entire template is processed.  Note this feature first appeared in Lift 2.2-M1.
  • Each node in the remaining template is processed via LiftSession.processSurroundAndInclude.  Any element in the lift or l namespace indicates a Snippet.  For example <lift:hello_world some="attr">kids</lift:hello_world> and <l:HelloWorld>kids<l:HelloWorld> are examples of snippet invocation. Snippets are functions that transform the kids of a snippet invocation into new nodes.  In code terms, a Snippet is NodeSeq => NodeSeq. See Snippet Name Resolution.
    • Attributes in the snippet tag will be available to the Snippet via the S.attr property.  Each snippet will treat the attributes differently.  Snippet attributes are retained in sub-snippet invocations.  This allows you to passed attributes to sub-snippets... for example <lift:surround foo_bar="baz"/>.  The foo_bar attribute will be available to snippets processed inside the current snippet.  This is useful for the Surround snippet and other eagerly evaluated snippets.
    • By default, the child nodes passed to a snippet are not themselves evaluated for snippets.  However, if you mark a snippet with lift:eager_eval="true", then the children are evaluated for snippet contents before being passed to the snippet function.
    • Snippets are evaluated in the order that they are encountered.  However, you may want to run certain snippets in parallel if they are accessing external resources such as an ad server.  To run a snippet in parallel, mark the snippet with lift:parallel="true" and the snippet will be run on a separate thread, but the page will not be returned to the browser until the snippet has completed its execution or a timeout has passed.  The timeout is set in LiftRules.lazySnippetTimeout which defaults to 30 seconds.
    • If you want to run a snippet and insert the value into the HTML page after the page has been rendered, use the <lift:LazyLoad/> snippet.
    • Snippets are processed recursively.  This means the the results of a snippet will then be scanned for other snippets.  This allows your snippet to return other snippets that will then be evaluated.
    • As of Lift 2.2-M1, snippets may also be invoked via attributes.  For example: <div class="bar l:snippet=hello_world?my_attr=foo"/>.  This will set the attribute "my_attr" to "foo", invoke the "hello_world" snippet and pass <div class="bar"/> as the parameter to the snippet.  This is more designer friendly.  Any class attributes prefixed by lift: or l: will be treated as a snippet.  If the snippet needs attributes, the attribute list is separated from the snippet name with a ? (question mark).  Attributes are name/value pairs separated by = (equals sign) and multiple attributes are separated by the choice of ; (semicolon), ? (question mark), or & (ampersand, which must be written as & for XML compatbility.)  Attribute names and values must be URL Encoded and properly XML escaped (thus, & turns into &).
  • After the snippets have been executed, Lift performs a merge phase.  During the merge phase:
    • All <head> elements in the <body> tag have their children moved to the <head> tag.  The children are de-dupped so that if 4 different references to the same css or script file occur in the <body>, only one of those tags is placed in the <head>.
    • All <tail> elements in the <body> are moved to the end of the <body> tag.
    • Lift inserts all appropriate JavaScript code if comet components appear on the page at the end of the <body> tag.
  • Lift tags the resulting document and converts it into an array of bytes, collects any headers and cookies and converts all of those into a LiftResponse.
  • As of Lift 2.2-M1, a subset of CSS selectors can be used snippets.  For example:
    "#name" #> userName // replace the element with the id name with the variable userName
    "#chat_lines *" #> listOfChats // replace the content of chat_lines with each element of listOfChats
    ".pretty *" #> <b>Unicorn</b> // each element with CSS class pretty, replace content with <b>Unicorn</b>
    "dog=cat [href]" #> "http://dogscape.com" // set the href attribute of all elements with the dog attribute set to cat
    "#name" #> userName & "#age" #> userAge // set name to userName and age to userAge

    So, a snippet can be as simple as:
    class Time {
      def render = "#time" #> Helpers.formattedTimeNow
    }

    And the snippet can be invoked:
    <span
    class="lift:Time" id="time">No time set</span>
  • or in head html part:
<object class="lift:LobbySnippet.checkChar"></object>