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: