Skip to content
Find file
Fetching contributors…
Cannot retrieve contributors at this time
3540 lines (2521 sloc) 63.8 KB
#LyX 1.6.7 created this file. For more info see http://www.lyx.org/
\lyxformat 345
\begin_document
\begin_header
\textclass book
\use_default_options false
\language english
\inputencoding auto
\font_roman default
\font_sans default
\font_typewriter default
\font_default_family default
\font_sc false
\font_osf false
\font_sf_scale 100
\font_tt_scale 100
\graphics default
\paperfontsize default
\spacing single
\use_hyperref false
\papersize default
\use_geometry false
\use_amsmath 1
\use_esint 1
\cite_engine basic
\use_bibtopic false
\paperorientation portrait
\secnumdepth 3
\tocdepth 3
\paragraph_separation indent
\defskip medskip
\quotes_language english
\papercolumns 1
\papersides 1
\paperpagestyle default
\tracking_changes false
\output_changes false
\author ""
\author ""
\end_header
\begin_body
\begin_layout Chapter
Lift and JavaScript
\begin_inset CommandInset label
LatexCommand label
name "cha:Lift-and-Javascript"
\end_inset
\end_layout
\begin_layout Standard
In this chapter we'll be discussing some of the techniques that Lift provides
for simplifying and abstracting access to JavaScript on the client side.
Using these facilities follows Lift's model of separating code from presentatio
n by allowing you to essentially write JavaScript code in Scala.
Lift also provides a layer that allows you to use advanced JavaScript functiona
lity via either the JQuery
\begin_inset Foot
status open
\begin_layout Plain Layout
\begin_inset CommandInset href
LatexCommand href
name "http://jquery.com/"
target "http://jquery.com/"
\end_inset
\end_layout
\end_inset
or YUI
\begin_inset Foot
status open
\begin_layout Plain Layout
\begin_inset CommandInset href
LatexCommand href
target "http://developer.yahoo.com/yui/"
\end_inset
\end_layout
\end_inset
user interface libraries.
\end_layout
\begin_layout Section
JavaScript high level abstractions
\end_layout
\begin_layout Standard
You may have noticed that Lift already comes with rich client side functionality
in the form of AJAX and COMET support (chapter
\begin_inset CommandInset ref
LatexCommand ref
reference "cha:AJAX-and-COMET"
\end_inset
).
Whenever you use this support, Lift automatically generates the proper
\family typewriter
<script>
\family default
elements in the returned page so that the libraries are included.
Lift goes one step further, however, by providing a class hierarchy representin
g JavaScript expressions.
For example, with an AJAX form element in Lift the callback method must
return JavaScript code to update the client side.
Instead of just returning a raw JavaScript string to be interpreted by
the client, you return an instance of the
\family typewriter
JsCmd
\family default
\begin_inset Foot
status open
\begin_layout Plain Layout
net.liftweb.http.js.JsCmd
\end_layout
\end_inset
trait (either directly or via implicit conversion) that is transformed
into the proper JavaScript for the client.
\end_layout
\begin_layout Standard
\family typewriter
JsCmd
\family default
represents a JavaScript command that can be executed on the client.
There is an additional
\begin_inset Quotes eld
\end_inset
base
\begin_inset Quotes erd
\end_inset
trait called
\family typewriter
JsExp
\family default
that represents a JavaScript expression.The differences between them are
not usually important to the developer, since a
\family typewriter
JsExp
\family default
instance is implicitly converted to a
\family typewriter
JsCmd
\family default
.
Also note that while Lift's JavaScript classes attempt to keep things type-safe
there are some limitations; in particular, Lift can't check semantic things
like whether the variable you're trying to access from a given
\family typewriter
JsCmd
\family default
actually exists.
Besides the obvious use in techniques like AJAX and COMET, Lift also makes
it simple to attach JavaScript to regular Scala XML objects, such as form
fields.
\end_layout
\begin_layout Standard
As a simple example, let's look at how we might add a simple alert to a
form if it doesn't validate.
In this example, we'll assume we have a
\family typewriter
name
\family default
form field that shouldn't be blank.
Listing
\begin_inset CommandInset ref
LatexCommand ref
reference "lst:Simple-Form-Validation"
\end_inset
shows a possible binding from our form snippet.
Let's break this down a bit: the first thing is that in order to reference
form elements (or any elements for that matter) from JavaScript, they need
to have an
\family typewriter
id
\family default
attribute.
We add the id attribute
\begin_inset Index
status open
\begin_layout Plain Layout
XML ! attributes
\end_layout
\end_inset
to our text field by passing a
\family typewriter
Pair[String,String]
\family default
.
Next, we need to define our actual validation.
We do this by adding some javascript to the
\family typewriter
onclick
\family default
attribute of our submit button.
The onclick attribute evaluates whatever javascript is assigned when the
button is clicked; if the javascript evaluates to true then submission
continues.
If it evaluates to false then submission is aborted.
In our case, we use the JsIf case class to check to see if the value of
our myName field is equal to an empty string.
In this case the JE object holds an implicit conversion from a Scala string
to a Str (JavaScript string) instance.
The second argument to JsIf is the body to be executed if the condition
is true.
In our case we want to pop up an alert to the user and stop form submission.
The JsCmd trait (which Alert mixes in) provides a
\begin_inset Quotes eld
\end_inset
&
\begin_inset Quotes erd
\end_inset
operator which allows you to chain multiple commands together.
Here we follow the Alert with a JsReturn, which returns the specified value;
again, there's an implicit conversion from Boolean to JsExp, so we can
simply provide the
\begin_inset Quotes eld
\end_inset
false
\begin_inset Quotes erd
\end_inset
value.
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Simple Form Validation
\begin_inset CommandInset label
LatexCommand label
name "lst:Simple-Form-Validation"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
import JsCmds._
\end_layout
\begin_layout Plain Layout
import JE._
\end_layout
\begin_layout Plain Layout
\end_layout
\begin_layout Plain Layout
var myName = ""
\end_layout
\begin_layout Plain Layout
bind(...
\end_layout
\begin_layout Plain Layout
"name" -> text(myName, myName = _, "id" -> "myName"),
\end_layout
\begin_layout Plain Layout
"submit" -> submit("Save", ..., "onclick" ->
\end_layout
\begin_layout Plain Layout
JsIf(JsEq(ValById("myName"), ""),
\end_layout
\begin_layout Plain Layout
Alert("You must provide a name") & JsReturn(false))
\end_layout
\begin_layout Plain Layout
)
\end_layout
\begin_layout Plain Layout
)
\end_layout
\end_inset
\end_layout
\begin_layout Subsection
JsCmd and JsExp overview
\end_layout
\begin_layout Standard
If you peruse the Lift API docs you'll find a large number of traits and
classes under the JsCmds and JE objects; these provide the vast majority
of the functionality you would need to write simple JavaScript code directly
in Lift.
Having said that, however, it's important to realize that the Lift classes
are intended to be used for small code fragments.
If you need to write large portions of JavaScript code for your pages,
we recommend writing that code in
\emph on
pure
\emph default
JavaScript in an external file and then including that file in your pages.
In particular, if you write your code as JavaScript functions, you can
use the
\family typewriter
JE.Call
\family default
class to execute those functions from your Lift code.
Table
\begin_inset CommandInset ref
LatexCommand ref
reference "tab:Top-Level-JsCmds"
\end_inset
gives a brief overview of the available JsCmds, while table
\begin_inset CommandInset ref
LatexCommand ref
reference "tab:Top-Level-JE-abstractions"
\end_inset
shows the JE expression abstractions.
\end_layout
\begin_layout Standard
\begin_inset Float table
placement H
wide false
sideways false
status open
\begin_layout Plain Layout
\align center
\begin_inset Tabular
<lyxtabular version="3" rows="22" columns="2">
<features islongtable="true">
<column alignment="left" valignment="top" width="1.25in">
<column alignment="left" valignment="bottom" width="4.5in">
<row endhead="true" endfirsthead="true">
<cell alignment="center" valignment="top" topline="true" bottomline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Command
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" bottomline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Description
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
After
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Executes the given JsCmd fragment after a given amount of time
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Alert
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Corresponds directly to the JavaScript alert function
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
CmdPair
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Executes two JsCmd fragments in order
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
FocusOnLoad
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Forces focus on the given XML element when the document loads
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Function
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Defines a JavaScript function with name, parameter list, and JsCmd body
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
JsBreak, JsContinue, JsReturn
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Corresponds directly to the JavaScript
\begin_inset Quotes eld
\end_inset
break
\begin_inset Quotes erd
\end_inset
,
\begin_inset Quotes eld
\end_inset
continue
\begin_inset Quotes erd
\end_inset
, and
\begin_inset Quotes eld
\end_inset
return
\begin_inset Quotes erd
\end_inset
keywords
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
JsFor, JsForIn, JsDoWhile, JsWhile
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
These define loop constructs in JavaScript with conditions and execution
bodies
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
JsHideId, JsShowId
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Hides or shows the HTML element with the given Id.
This is actually handled via the LiftArtifacts' hide and show methods
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
JsIf
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Corresponds to the JavaScript
\begin_inset Quotes eld
\end_inset
if
\begin_inset Quotes erd
\end_inset
statement, with a condition, body to execute if the condition is true,
and optional
\begin_inset Quotes eld
\end_inset
else
\begin_inset Quotes erd
\end_inset
body statement
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
JsTry
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Defines a try/catch block tha can optionally alert if an exception is caught
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
JsWith
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Defines a with statement to reduce object references
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
OnLoad
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Defines a JavaScript statement that is executed on page load
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Noop
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Defines an empty JavaScript statement
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
RedirectTo
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Uses window.location to redirect to a new page
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
ReplaceOptions
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Replaces options on a form Select with a new list of options.
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Run
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Executes the given string as raw javascript
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Script
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Defines a <script> element with proper CDATA escaping, etc to conform to
XHTML JavaScript support
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
SetElemById
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Assigns a statement to a given element by id.
Optional parameters allow you to specify properties on the element
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
SetExp
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Defines an assignment to an arbitrary JavaScript expression from another
JavaScript expression
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
SetHtml
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Sets the contents of a given HTML node by Id to a given NodeSeq.
This is especially useful in Ajax calls that update parts of the page
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" bottomline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
SetValById
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" bottomline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Defines an assignment to a given element's
\begin_inset Quotes eld
\end_inset
value
\begin_inset Quotes erd
\end_inset
property
\end_layout
\end_inset
</cell>
</row>
</lyxtabular>
\end_inset
\end_layout
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Basic JsCmds
\begin_inset CommandInset label
LatexCommand label
name "tab:Top-Level-JsCmds"
\end_inset
\end_layout
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Standard
\begin_inset Float table
placement H
wide false
sideways false
status open
\begin_layout Plain Layout
\align center
\begin_inset Tabular
<lyxtabular version="3" rows="18" columns="2">
<features islongtable="true">
<column alignment="left" valignment="top" width="1.5in">
<column alignment="left" valignment="top" width="4in">
<row endhead="true" endfirsthead="true">
<cell alignment="center" valignment="top" topline="true" bottomline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Expression
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" bottomline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Description
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
AnonFunc
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Defines an anonymous JavaScript function
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Call
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Calls a JavaScript function by name, with parameters
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
ElemById
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Obtains a DOM element by its Id, with optional property access
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
FormToJson
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Converts a given form (by Id) into a JSON representation
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Id, Style, Value
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Represents the
\begin_inset Quotes eld
\end_inset
id
\begin_inset Quotes erd
\end_inset
,
\begin_inset Quotes eld
\end_inset
style
\begin_inset Quotes erd
\end_inset
and
\begin_inset Quotes eld
\end_inset
value
\begin_inset Quotes erd
\end_inset
element attributes
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
JsArray
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Constructs a JavaScript array from a given set of JavaScript expressions
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
JsEq, JsNotEq, JsGt, JsGtEq, JsLt, JsLtEq
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Comparison tests between two JavaScript expressions.
JsExp instances also have a
\begin_inset Quotes eld
\end_inset
===
\begin_inset Quotes erd
\end_inset
operator which is equivalent to JsEq
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
JsTrue, JsFalse, JsNull
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Represents the
\begin_inset Quotes eld
\end_inset
true
\begin_inset Quotes erd
\end_inset
,
\begin_inset Quotes eld
\end_inset
false
\begin_inset Quotes erd
\end_inset
, and
\begin_inset Quotes eld
\end_inset
null
\begin_inset Quotes erd
\end_inset
values
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
JsFunc
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Similar to Call; executes a JavaScript function
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
JsObj
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Represents a JavaScript object with a Map for properties
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
JsRaw
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Represents a raw JavaScript fragment.
You can use this if Lift doesn't provide functionality via abstractions
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
JsVal
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Represents an abritrary JavaScript value
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
JsVar
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Represents a JavaScript variable, with optional property access
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Num
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Represents a JavaScript number.
JE contains implicit conversions from Scala numeric types to Num
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Str
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Represents a Javascript String.
JE contains implicit conversions from a Scala String to Str
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Stringify
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Calls JSON.stringify to convert a JavaScript object into a JSON string representa
tion
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" bottomline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
ValById
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" bottomline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
\size small
Represents the
\begin_inset Quotes eld
\end_inset
value
\begin_inset Quotes erd
\end_inset
property of a given element by Id
\end_layout
\end_inset
</cell>
</row>
</lyxtabular>
\end_inset
\end_layout
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Basic JE abstractions
\begin_inset CommandInset label
LatexCommand label
name "tab:Top-Level-JE-abstractions"
\end_inset
\end_layout
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Subsection
JavaScript Abstraction Examples
\end_layout
\begin_layout Standard
As you can see, Lift provides a large coverage of JavaScript functionality
through its abstraction layer.
Even if you've done a lot of JavaScript, however, the abstractions don't
always map one-to-one and it can take some effort to wrap your head around
it.
We're going to provide a few examples to help you understand how it works.
We'll start off with a simple example of an Ajax callback (Ajax is covered
in chapter
\begin_inset CommandInset ref
LatexCommand ref
reference "cha:AJAX-and-COMET"
\end_inset
).
Listing
\begin_inset CommandInset ref
LatexCommand ref
reference "lst:Using-SetHtml"
\end_inset
shows how we can update an HTML element with new content via the Ajax call.
In this case, we're changing a chart image based on some passed parameters.
Our HTML needs to contain an element with an id of
\begin_inset Quotes eld
\end_inset
tx_graph
\begin_inset Quotes erd
\end_inset
; this element will have its children
\emph on
replaced
\emph default
with whatever NodeSeq we pass as the second argument.
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Using SetHtml
\begin_inset CommandInset label
LatexCommand label
name "lst:Using-SetHtml"
\end_inset
\end_layout
\end_inset
def updateGraph() = {
\end_layout
\begin_layout Plain Layout
val dateClause : String = ...
\end_layout
\begin_layout Plain Layout
val url = "/graph/" + acctName + "/" + graphType + dateClause
\end_layout
\begin_layout Plain Layout
JsCmds.SetHtml("tx_graph", <img src={url} />)
\end_layout
\begin_layout Plain Layout
}
\end_layout
\end_inset
\end_layout
\begin_layout Standard
As a more complex example, we could add some JavaScript behavior combining
Ajax with some client-side state, as shown in listing
\begin_inset CommandInset ref
LatexCommand ref
reference "lst:Client-side-comparisons"
\end_inset
.
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Client-side comparisons
\begin_inset CommandInset label
LatexCommand label
name "lst:Client-side-comparisons"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
import js.JE._ // for implicit conversions
\end_layout
\begin_layout Plain Layout
def moreComplexCallback (value : String) = {
\end_layout
\begin_layout Plain Layout
JsIf(ValById("username") === value.toLowerCase, {
\end_layout
\begin_layout Plain Layout
JsFunc("logAccess", "Self-share attempted").cmd & Alert("You can't share
with yourself!")
\end_layout
\begin_layout Plain Layout
})
\end_layout
\begin_layout Plain Layout
}
\end_layout
\end_inset
\end_layout
\begin_layout Section
JQuery and other JavaScript frameworks
\end_layout
\begin_layout Standard
We've mentioned earlier that Lift uses the JQuery JavaScript framework by
default.
Lift wouldn't be Lift, however, if it didn't provide a mechanism for using
other frameworks.
The way that lift determines which JavaScript framework to use is via the
\family typewriter
JSArtifacts
\family default
\begin_inset Foot
status open
\begin_layout Plain Layout
net.liftweb.http.js.JSArtifacts
\end_layout
\end_inset
trait along with the LiftRules.jsArtifacts var.
Lift comes with two default implementations of JSArtifacts:
\family typewriter
JQueryArtifacts
\begin_inset Foot
status open
\begin_layout Plain Layout
net.liftweb.http.js.jquery.JQueryArtifacts
\end_layout
\end_inset
\family default
and
\family typewriter
YUIArtifacts
\family default
\begin_inset Foot
status open
\begin_layout Plain Layout
net.liftweb.http.js.yui.YUIArtifacts
\end_layout
\end_inset
.
If you want to use a different framework, you must provide a concrete implement
ation of the JSArtifacts trait specific to that framework.
The JQuery support in Lift extends beyond just the JSArtifacts, support;
there are also a number of JSExp and JsCmd traits and classes in the
\family typewriter
net.liftweb.http.js.jquery
\family default
package that provide JQuery specific implementations for standard expressions
and commands.
\end_layout
\begin_layout Standard
Changing one implementation or another can be done from LiftRules.jsArtifacts
variable, which by default points to JQueryArtifacts.
Typically this is done in Boot, as shown in listing
\begin_inset CommandInset ref
LatexCommand ref
reference "lst:Configuring-Lift-YUI"
\end_inset
.
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Configuring Lift YUI
\begin_inset CommandInset label
LatexCommand label
name "lst:Configuring-Lift-YUI"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
import net.liftweb.http.js.yui.YUIArtifacts
\end_layout
\begin_layout Plain Layout
\end_layout
\begin_layout Plain Layout
class Boot {
\end_layout
\begin_layout Plain Layout
def boot = {
\end_layout
\begin_layout Plain Layout
...
\end_layout
\begin_layout Plain Layout
LiftRules.jsArtifacts = YUIArtifacts
\end_layout
\begin_layout Plain Layout
...
\end_layout
\begin_layout Plain Layout
}
\end_layout
\end_inset
\end_layout
\begin_layout Standard
In addition to changing LiftRules, you also need to take into account that
other frameworks have their own scripts and dependencies that you'll need
to include in your pages.
For YUI you would need to include the following scripts (at minimum):
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Lift YUI scripts
\begin_inset CommandInset label
LatexCommand label
name "lst:Lift-YUI-scripts"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
<script src="/classpath/yui/yahoo.js" type="text/javascript"/>
\end_layout
\begin_layout Plain Layout
<script src="/classpath/yui/event.js" type="text/javascript"/>
\end_layout
\begin_layout Plain Layout
<script src="/classpath/yui/dom.js" type="text/javascript"/>
\end_layout
\begin_layout Plain Layout
<script src="/classpath/yui/connection.js" type="text/javascript"/>
\end_layout
\begin_layout Plain Layout
<script src="/classpath/yui/json.js" type="text/javascript"/>
\end_layout
\begin_layout Plain Layout
<script src="/classpath/liftYUI.js" type="text/javascript"/>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
Of course, to keep things simple you could either place all of these items
in a template that you could embed, or you could combine the files into
a single JavaScript source file.
\end_layout
\begin_layout Standard
We have some simple recommendations on using different JavaScript frameworks
from within Lift:
\end_layout
\begin_layout Enumerate
If you don't necessarily need YUI widgets or if you can find similar functionali
ty in JQuery plugins, we recommend using the JQuery framework.
Lift provides much better support out-of-the-box for JQuery
\end_layout
\begin_layout Enumerate
Do not mix JQuery and YUI unless you really know what you are doing.
Getting both of them together leads to a number of collisions.
\end_layout
\begin_layout Section
XML and JavaScript
\end_layout
\begin_layout Standard
What we've covered so far is pretty much standard JavaScript behind some
Lift facades.
There are situations, however, when you want to do things that are complicated
or outside the scope of typical JavaScript functionality.
One example of this is when you need to build dynamic DOM elements from
JavaScript code, say to build an HTML list.
Lift has a very nice way of dealing with such situation; with a few lines
of code you can achieve quite a lot.
The main functionality for this is provided via the Jx* classes
\begin_inset Foot
status open
\begin_layout Plain Layout
net.liftweb.http.js.Jx, etc
\end_layout
\end_inset
, which you can use to transform a scala.xml.NodeSeq into javascript code
that generates the corresponding nodes on the client side.
Listing
\begin_inset CommandInset ref
LatexCommand ref
reference "lst:Jx-trivial-example"
\end_inset
shows a simple example of emitting a div on a page via JavaScript.
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Jx trivial example
\begin_inset CommandInset label
LatexCommand label
name "lst:Jx-trivial-example"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
import net.liftweb.http.js._
\end_layout
\begin_layout Plain Layout
import JE._
\end_layout
\begin_layout Plain Layout
\end_layout
\begin_layout Plain Layout
val div = Jx(<div>Hi there</div>)
\end_layout
\end_inset
\end_layout
\begin_layout Standard
This code generates the following JavaScript code:
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Jx Emitted Code
\begin_inset CommandInset label
LatexCommand label
name "lst:Jx-Emitted-Code"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
function(it) {
\end_layout
\begin_layout Plain Layout
var df = document.createDocumentFragment();
\end_layout
\begin_layout Plain Layout
var vINIJ1YTZG5 = document.createElement('div');
\end_layout
\begin_layout Plain Layout
df.appendChild(vINIJ1YTZG5);
\end_layout
\begin_layout Plain Layout
vINIJ1YTZG5.appendChild(document.createTextNode('Hi there'));
\end_layout
\begin_layout Plain Layout
return df;
\end_layout
\begin_layout Plain Layout
}
\end_layout
\end_inset
\end_layout
\begin_layout Standard
As you can see, Lift took our XML code and transformed it into a JavaScript
function that dynamically creates a document fragment containing the given
NodeSeq.
The
\family typewriter
it
\family default
parameter can be any JavaScript object; we'll cover how you use it in a
moment.
The name of the
\emph on
var
\emph default
is automatically and randomly generated to ensure uniqueness.
\end_layout
\begin_layout Standard
Of course, if that was all Lift was doing that's not much help.
At this point we've only generated a function that generates XML.
Let's take a look on a more complex example that shows the real power of
the Jx classes.
Assume we have a JSON structure that contains an array of objects containing
firstName and lastName properties.
This JSON structure could look something like:
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Sample JSON Structure
\begin_inset CommandInset label
LatexCommand label
name "lst:Sample-JSON-Structure"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
var list = {
\end_layout
\begin_layout Plain Layout
persons: [
\end_layout
\begin_layout Plain Layout
{name: "Thor", race: "Asgard"},
\end_layout
\begin_layout Plain Layout
{name: "Todd", race: "Wraith"},
\end_layout
\begin_layout Plain Layout
{name: "Rodney", race: "Human"}
\end_layout
\begin_layout Plain Layout
]
\end_layout
\begin_layout Plain Layout
}
\end_layout
\begin_layout Plain Layout
// Guess what I've been watching lately ?
\end_layout
\end_inset
\end_layout
\begin_layout Standard
Now we can use a combination of Jx classes to render this content as an
HTML dynamic list:
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Rendering a JSON List Via Jx
\begin_inset CommandInset label
LatexCommand label
name "lst:Rendering-a-JSON-list"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
def renderPerson =
\end_layout
\begin_layout Plain Layout
Jx(<li class="item_header"> {JsVar("it", "name")}
\end_layout
\begin_layout Plain Layout
is {JsVar("it", "race")}</li>)
\end_layout
\begin_layout Plain Layout
Jx(<ul>{JxMap(JsVar("it.persons"), renderPerson)}</ul>)
\end_layout
\end_inset
\end_layout
\begin_layout Standard
Well what this code does is this:
\end_layout
\begin_layout Enumerate
Construct an <ul> list that contains a bunch of elements
\end_layout
\begin_layout Enumerate
JxMap takes a JavaScript object, in this case
\family typewriter
it.persons
\family default
(remember
\family typewriter
it
\family default
is the parameter of the generated function), and iterate for each element
of the array and apply the renderPerson function.
Of course each element of the array will be a JSON object containing name
and race properties.
\end_layout
\begin_layout Enumerate
The
\family typewriter
renderPerson
\family default
function generates a JavaScript function as we've already shown, and renders
the JavaScript code that generates the <li> elements containing the name
value followed by
\begin_inset Quotes eld
\end_inset
is
\begin_inset Quotes erd
\end_inset
followed by the race value.
\end_layout
\begin_layout Enumerate
If we send this generated JavaScript function to client and calling it by
pass the
\emph on
list
\emph default
variable above It will create the following document fragment:
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
<ul>
\end_layout
\begin_layout Plain Layout
<li class="item_header">Thor is Asgard</li>
\end_layout
\begin_layout Plain Layout
<li class="item_header">Todd is Wraith</li>
\end_layout
\begin_layout Plain Layout
<li class="item_header">Rodney is Human</li>
\end_layout
\begin_layout Plain Layout
</ul>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
With a couple of lines of code we've managed to generate the JavaScript
code that creates document fragments dynamically.
Here is the list of JX classes that you may find interesting:
\end_layout
\begin_layout Standard
\align center
\begin_inset Tabular
<lyxtabular version="3" rows="8" columns="2">
<features>
<column alignment="left" valignment="top" width="0">
<column alignment="left" valignment="top" width="4in">
<row>
<cell alignment="center" valignment="top" topline="true" bottomline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
Class
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" bottomline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
Description
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
JxBase
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
The parent trait for all other Jx classes
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
JxMap
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
Iterates over a JavaScript array and applies a function on each element
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
JxMatch
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
Match a JsExp against a sequence of JsCase
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
JxCase
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
Contains a JsExp for matching purposes and the NodeSeq to be applied in
case the matching succeeds
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
JxIf
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
Contains a JsExp and a NodeSeq to be applied only if JsExp is evaluated
to true
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
JxIfElse
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
Similar with JxIf but it contains the else branch
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" bottomline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
Jx
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" bottomline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
The basic application of the transformation from a NodeSeq to the JavaScript
code
\end_layout
\end_inset
</cell>
</row>
</lyxtabular>
\end_inset
\end_layout
\begin_layout Section
JSON
\begin_inset Note Note
status open
\begin_layout Plain Layout
Need to add details on lift-json as well
\end_layout
\end_inset
\end_layout
\begin_layout Standard
JSON
\begin_inset Foot
status open
\begin_layout Plain Layout
Java Script Object Notation -
\begin_inset CommandInset href
LatexCommand href
target "http://www.json.org"
\end_inset
\end_layout
\end_inset
is a way of structuring information in JavaScript code.
One of its most common uses is to represent structured information on the
wire.
One example would be a JavaScript AJAX API where the server response is
in fact a JSON construct.
Let's look at an example first in listing
\begin_inset CommandInset ref
LatexCommand ref
reference "lst:Ajax-JSON-response"
\end_inset
:
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Ajax JSON response
\begin_inset CommandInset label
LatexCommand label
name "lst:Ajax-JSON-response"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
class SimpleSnippet {
\end_layout
\begin_layout Plain Layout
def ajaxFunc() : JsCmd = {
\end_layout
\begin_layout Plain Layout
JsCrVar("myObject", JsObj(("persons", JsArray(
\end_layout
\begin_layout Plain Layout
JsObj(("name", "Thor"), ("race", "Asgard")),
\end_layout
\begin_layout Plain Layout
JsObj(("name", "Todd"), ("race", "Wraith")),
\end_layout
\begin_layout Plain Layout
JsObj(("name", "Rodney"), ("race", "Human"))
\end_layout
\begin_layout Plain Layout
)))) & JsRaw("alert(myObject.persons[0].name)")
\end_layout
\begin_layout Plain Layout
}
\end_layout
\begin_layout Plain Layout
\end_layout
\begin_layout Plain Layout
def renderAjaxButton(xhtml: Group): NodeSeq = {
\end_layout
\begin_layout Plain Layout
bind("ex", xhtml,
\end_layout
\begin_layout Plain Layout
"button" -> SHtml.ajaxButton(Text("Press me"), ajaxFunc _))
\end_layout
\begin_layout Plain Layout
}
\end_layout
\begin_layout Plain Layout
}
\end_layout
\end_inset
\end_layout
\begin_layout Standard
Your template would look like listing
\begin_inset CommandInset ref
LatexCommand ref
reference "lst:AJAX-Template"
\end_inset
:
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
AJAX Template
\begin_inset CommandInset label
LatexCommand label
name "lst:AJAX-Template"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
...
\end_layout
\begin_layout Plain Layout
<lift:SimpleSnippet.renderAjaxButton>
\end_layout
\begin_layout Plain Layout
<ex:button/>
\end_layout
\begin_layout Plain Layout
</lift:SimpleSnippet.renderAjaxButton>
\end_layout
\begin_layout Plain Layout
...
\end_layout
\end_inset
\end_layout
\begin_layout Standard
First off, we have a simple snippet function called
\family typewriter
renderAjaxButton
\family default
.
Here we're binding the ex:button tag and render a XHTML button tag that
when pressed will send an Ajax request to server.
When this request is received, the
\family typewriter
ajaxFunc
\family default
is executed and the JsCmd response is turned into a JavaScript content
type response.
In
\family typewriter
ajaxFunc
\family default
we construct a JSON object (the same one we used previously for the persons
object).
We assign the JSON structure to the JavaScript variable
\family typewriter
myObject
\family default
and them call alert on the first element on the persons object.
The rendered JavaScript code that will be send down the wire will be:
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Generated JavaScript
\begin_inset CommandInset label
LatexCommand label
name "lst:Generated-JavaScript"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
var myObject = {'persons': [{'name': 'Thor', 'race': 'Asgard'},
\end_layout
\begin_layout Plain Layout
{'name': 'Todd', 'race': 'Wraith'} ,
\end_layout
\begin_layout Plain Layout
{'name': 'Rodney', 'race': 'Human'}]};
\end_layout
\begin_layout Plain Layout
alert(myObject.persons[0].name);
\end_layout
\end_inset
\end_layout
\begin_layout Standard
So in your page when you press the button you'll get an alert dialog saying
\begin_inset Quotes eld
\end_inset
Thor
\begin_inset Quotes erd
\end_inset
.
Here we used the JsRaw class which basically renders the exact thing you
passed to it: raw JavaScript code.
\end_layout
\begin_layout Subsection
JSON forms
\begin_inset CommandInset label
LatexCommand label
name "sub:JSON-forms"
\end_inset
\end_layout
\begin_layout Standard
Now that we've covered sending JSON from the server to the client, let's
look at going in the opposite direction.
Lift provides a mechanism for sending form data to the server encapsulated
in a JSON object.
In and of itself sending the data in JSON format is relatively simple;
where Lift really adds value is via the JsonHandler
\begin_inset Foot
status open
\begin_layout Plain Layout
net.liftweb.http.JsonHandler
\end_layout
\end_inset
class.
This class provides a framework for simplifying processing of submitted
JSON data.
To start, let's look at some example template code for a JSON form:
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
A Simple JSON form
\begin_inset CommandInset label
LatexCommand label
name "lst:A-Simple-JSON-form"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
<lift:surround with="default" at="content">
\end_layout
\begin_layout Plain Layout
<lift:JSONForm.head />
\end_layout
\begin_layout Plain Layout
<lift:JSONForm.show>
\end_layout
\begin_layout Plain Layout
<input type="text" name="name" />
\end_layout
\begin_layout Plain Layout
<br />
\end_layout
\begin_layout Plain Layout
<input type="text" name="value" />
\end_layout
\begin_layout Plain Layout
<br />
\end_layout
\begin_layout Plain Layout
<input type="radio" name="vehicle" value="Bike" />
\end_layout
\begin_layout Plain Layout
<input type="radio" name="vehicle" value="Car" />
\end_layout
\begin_layout Plain Layout
<input type="radio" name="vehicle" value="Airplane" />
\end_layout
\begin_layout Plain Layout
<br />
\end_layout
\begin_layout Plain Layout
<select name="cars">
\end_layout
\begin_layout Plain Layout
<option value="volvo">Volvo</option>
\end_layout
\begin_layout Plain Layout
<option value="saab">Saab</option>
\end_layout
\begin_layout Plain Layout
<option value="opel">Opel</option>
\end_layout
\begin_layout Plain Layout
<option value="audi">Audi</option>
\end_layout
\begin_layout Plain Layout
</select>
\end_layout
\begin_layout Plain Layout
<button type="submit">Submit</button>
\end_layout
\begin_layout Plain Layout
</lift:JSONForm.show>
\end_layout
\begin_layout Plain Layout
<div id="json_result"></div>
\end_layout
\begin_layout Plain Layout
</lift:surround>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
A you can see, the XHTML template is relatively straightforward.
The Snippet code is where things really get interesting:
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
JSON Form Snippet Code
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
class JSONForm {
\end_layout
\begin_layout Plain Layout
def head =
\end_layout
\begin_layout Plain Layout
<head>
\end_layout
\begin_layout Plain Layout
<script type="text/javascript"
\end_layout
\begin_layout Plain Layout
src={"/" + LiftRules.resourceServerPath + "/jlift.js"} />
\end_layout
\begin_layout Plain Layout
{Script(json.jsCmd)}
\end_layout
\begin_layout Plain Layout
</head>
\end_layout
\begin_layout Plain Layout
\end_layout
\begin_layout Plain Layout
def show(html: Group): NodeSeq = {
\end_layout
\begin_layout Plain Layout
SHtml.jsonForm(json, html)
\end_layout
\begin_layout Plain Layout
}
\end_layout
\begin_layout Plain Layout
\end_layout
\begin_layout Plain Layout
import JsCmds._
\end_layout
\begin_layout Plain Layout
object json extends JsonHandler {
\end_layout
\begin_layout Plain Layout
def apply(in: Any): JsCmd = SetHtml("json_result", in match {
\end_layout
\begin_layout Plain Layout
case JsonCmd("processForm", _, p: Map[String, _], _) => {
\end_layout
\begin_layout Plain Layout
// process the form or whatever
\end_layout
\begin_layout Plain Layout
println("Cars = " + urlDecode(p("cars")))
\end_layout
\begin_layout Plain Layout
println("Name = " + urlDecode(p("name")))
\end_layout
\begin_layout Plain Layout
<b>{p}</b>
\end_layout
\begin_layout Plain Layout
}
\end_layout
\begin_layout Plain Layout
case x => <b>Problem...
didn't handle JSON message {x}</b>
\end_layout
\begin_layout Plain Layout
})
\end_layout
\begin_layout Plain Layout
}
\end_layout
\begin_layout Plain Layout
}
\end_layout
\end_inset
\end_layout
\begin_layout Standard
The first thing we define is the
\family typewriter
head
\family default
function.
Its purpose is simply to generate the JavaScript functions that set up
the form handling on the client side.
That means that when the submit button is clicked, the contents of the
form are turned into JSON and submitted via an Ajax call to the server.
The
\family typewriter
show
\family default
function defines the connection between the concrete JsonHandler instance
that will process the form and the template HTML that contains the form.
We perform this binding with the
\family typewriter
SHtml.jsonForm
\family default
method.
This wraps the HTML with a
\family typewriter
<form>
\family default
tag and sets the
\family typewriter
onsubmit
\family default
event to do JSON bundling.
\end_layout
\begin_layout Standard
The key part of the equation is our JsonHandler object.
The apply method is what will be called when the JSON object is submitted
to the server.
If the JSON is properly parsed then you'll get a JsonCmd instance which
you can use Scala's matching to pick apart.
The apply function needs to return a JsCmd (JavaScript code), which in
this case sets the HTML content of the
\family typewriter
json_result
\family default
div element.
When the form is stringified into its JSON representation Lift uses a command
property indicating the action that needs to be done on server and the
actual JSON data.
In the case of JSON forms the command is always
\begin_inset Quotes eld
\end_inset
processForm
\begin_inset Quotes erd
\end_inset
as this is important for pattern matching as seen above.
The actual form content is a Map object that can be easily use to obtain
the values for each form field.
\end_layout
\begin_layout Section
JqSHtml object
\end_layout
\begin_layout Standard
SHtml generated code is independent on the JavaScript framework used.
However
\family typewriter
net.liftweb.http.jquery.JsSHtml
\family default
object contains artifacts that are bound with JQuery framework.
For instance it contains the autocomplete function that renders an input
type text element but when start typing it will suggest words starting
with what you typed already.
Please see
\begin_inset CommandInset href
LatexCommand href
target "http://www.pengoworks.com/workshop/jquery/autocomplete.htm"
\end_inset
for examples.
\end_layout
\begin_layout Section
A recap
\end_layout
\begin_layout Standard
We've seen so far how we can abstract JavaScript code at Scala level using
Lift's JS abstraction.
You can model endless cases by using these abstractions.
But let's take a look on another example a bit more complex.
It is about a fast search where you have a text box and when you hit enter
it will return the list of items that contain that sequence.
The list of items will be rendered in a DIV real estate.
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Example template
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
\end_layout
\begin_layout Plain Layout
<lift:surround with="default" at="content">
\end_layout
\begin_layout Plain Layout
<lift:Hello.ajaxian>
\end_layout
\begin_layout Plain Layout
<text:show/>
\end_layout
\begin_layout Plain Layout
</lift:Hello.ajaxian>
\end_layout
\begin_layout Plain Layout
<div id="items_list" style="width: 300px; height: 100px; overflow: auto;
border: 1px solid black;">
\end_layout
\begin_layout Plain Layout
</div>
\end_layout
\begin_layout Plain Layout
</lift:surround>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
So we just have a really simple snippet and the div placeholder.
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Example snippet
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
\end_layout
\begin_layout Plain Layout
import JE._
\end_layout
\begin_layout Plain Layout
import net.liftweb.http.js.jquery.JqJE._
\end_layout
\begin_layout Plain Layout
import net.liftweb.http.SHtml._
\end_layout
\begin_layout Plain Layout
import net.liftweb.util.Helpers._
\end_layout
\begin_layout Plain Layout
import JsCmds._
\end_layout
\begin_layout Plain Layout
\end_layout
\begin_layout Plain Layout
val names = "marius" :: "tyler" :: "derek" :: "dave" :: "jorge" :: "viktor"
:: Nil
\end_layout
\begin_layout Plain Layout
\end_layout
\begin_layout Plain Layout
def ajaxian(html: Group) : NodeSeq = {
\end_layout
\begin_layout Plain Layout
bind("text", html,
\end_layout
\begin_layout Plain Layout
"show" -> ajaxText("Type something", {value => {
\end_layout
\begin_layout Plain Layout
val matches = names.filter(e => e.indexOf(value) > -1)
\end_layout
\begin_layout Plain Layout
SetHtml("items_list", NodeSeq.Empty) &
\end_layout
\begin_layout Plain Layout
JsCrVar("items", JsArray(matches.map(Str(_)):_*)) &
\end_layout
\begin_layout Plain Layout
JsCrVar("func", Jx(<ul>{
\end_layout
\begin_layout Plain Layout
JxMap(JsVar("it"), Jx(<li><a href="">{JsVar(
"it")}</a></li>)) }
</ul>).toJs) &
\end_layout
\begin_layout Plain Layout
(ElemById("items_list") ~> JsFunc("appendChild", Call("func",
JsVar("items"))))
\end_layout
\begin_layout Plain Layout
}})
\end_layout
\begin_layout Plain Layout
)
\end_layout
\begin_layout Plain Layout
}
\end_layout
\end_inset
\end_layout
\begin_layout Standard
The part with the snippet is probably already familiar to you.
We are calling the ajaxText function which renders an input text element.
When you hit enter an Ajax request will be sent and the anonymous function
that we bound here will be executed.
Here is what happens:
\end_layout
\begin_layout Enumerate
First filter out the names that contain the provided value in the input
text.
So all element that contain that sequence.
\end_layout
\begin_layout Enumerate
Then return a JsExp that we are building:
\end_layout
\begin_deeper
\begin_layout Enumerate
SetHtml is clearing out the div element that we're using as a real estate
for our search results list
\end_layout
\begin_layout Enumerate
Then we re declaring a JavaScript variable which is an array containing
the resulting items that matched the search criteria.
\end_layout
\begin_layout Enumerate
Then we are declaring thr
\family typewriter
func
\family default
variable which obviously is a function.
We've seen above how to use the Jx artifacts.
Now we are building a html list (
\family typewriter
<ul>
\family default
) that for each element from the
\family typewriter
it
\family default
variable will build the
\family typewriter
<li>
\family default
sequences.
The it variable is actually the paramter that this function takes which
is the items array that we declared above.
\end_layout
\begin_layout Enumerate
After that we are obtaining the HTML node denominated by
\begin_inset Quotes eld
\end_inset
items_list
\begin_inset Quotes erd
\end_inset
id and call appendChild function of the Node object.
The ~> function is use to call functions of objects.
Of course to the appendChild function we need to provide a parameter.
This parameter is the document fragment returned by
\family typewriter
func
\family default
function.
When we are caling the
\family typewriter
func
\family default
function we are passing items variable decalred above.
\end_layout
\end_deeper
\begin_layout Standard
As you noticed already we composed a small JavaScript code by chainin multiple
JS expressions/commands using the & function.
\end_layout
\end_body
\end_document
Something went wrong with that request. Please try again.