Permalink
Find file
Fetching contributors…
Cannot retrieve contributors at this time
2284 lines (1555 sloc) 38.6 KB
#LyX 1.6.1 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 Widgets
\begin_inset CommandInset label
LatexCommand label
name "cha:Lift-Widgets"
\end_inset
\end_layout
\begin_layout Standard
In this chapter we're going to discuss widgets in Lift.
A widget is essentially a library of Scala and JavaScript code that together
provide packaged XHTML fragments for display on the client browser.
In other web frameworks (JSF, Struts, etc) these are sometimes called component
s.
An example of a widget would be small library that automatically embeds
a Calendar instance (section
\begin_inset CommandInset ref
LatexCommand ref
reference "sub:Calendar-widgets"
\end_inset
), or a helper library to sort HTML tables (section
\begin_inset CommandInset ref
LatexCommand ref
reference "sub:TableSorter-widget"
\end_inset
).
Typically widgets embody dynamic behavior on the client side, which is
what makes them so attractive; static client-side content is already dead
simple to generate in Lift with snippets, so the extra sauce of JavaScript
binding and Ajax callbacks really makes advanced functionality easy.
\end_layout
\begin_layout Standard
Lift's widgets are intended to minimize effort on your part.
Unlike some other frameworks where widgets/components require the use of
specific traits or special XML binding, Lift (and Scala's) inherent flexibility
with XML, JavaScript abstraction, and snippet generators make using widgets
as simple as dropping in a few lines of code to your existing snippets
or views.
\end_layout
\begin_layout Section
Current Lift Widgets
\end_layout
\begin_layout Standard
To start, we'll cover the current set of widgets included in Lift at the
time of writing this book.
These widgets are contained in the lift-widgets module, which means you'll
need to add the dependency to your pom.xml if you want to use them (section
\begin_inset CommandInset ref
LatexCommand ref
reference "sub:Adding-a-Dependency"
\end_inset
).
While this list will likely grow over time, remember that widgets are based
on the fundamentals of Scala's XML functionality as well as Lift's JavaScript
support (chapter
\begin_inset CommandInset ref
LatexCommand ref
reference "cha:Lift-and-Javascript"
\end_inset
), so the same general rules apply to all of them.
At the end of the chapter we'll cover writing your own widgets (section
\begin_inset CommandInset ref
LatexCommand ref
reference "sec:How-to-build-widgets"
\end_inset
).
\end_layout
\begin_layout Subsection
TableSorter widget
\begin_inset CommandInset label
LatexCommand label
name "sub:TableSorter-widget"
\end_inset
\end_layout
\begin_layout Standard
\begin_inset Float figure
wide false
sideways false
status open
\begin_layout Plain Layout
\align center
\begin_inset Graphics
filename images/tablesorter.png
width 5in
\end_inset
\end_layout
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
TableSorter widget
\end_layout
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Standard
The TableSorter widget is based on the TableSorter jQuery plugin
\begin_inset Foot
status open
\begin_layout Plain Layout
\begin_inset CommandInset href
LatexCommand href
target "http://tablesorter.com/docs/"
\end_inset
\end_layout
\end_inset
.
Basically, the TableSorter widget allows you to take an existing HTML table
(THEAD and TBODY tags are required) and add sorting to columns in the table.
By default, the widget handles sorting of numeric, currency, and other
value types automatically.
The full capabilities of the plugin are beyond the scope of the widget,
however; if you need more features you'll have to set up the JavaScript
yourself instead of using the widget.
\end_layout
\begin_layout Standard
The first step in using the widget is to call the
\family typewriter
TableSorter.init
\family default
function in your Boot class to make Lift aware of the resources used by
this widget.
Then, you need to set up a table in your page (either statically in the
template or via a snippet):
\end_layout
\begin_layout Standard
\begin_inset listings
lstparams "language=XML"
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
TableSorter Template
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
<lift:surround with="default" at="content">
\end_layout
\begin_layout Plain Layout
<lift:TableSorterDemo/>
\end_layout
\begin_layout Plain Layout
<table id=
"
table-id
"
class=
"
tablesorter
"
> ...
</table>
\end_layout
\begin_layout Plain Layout
</lift:surround>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
Note that you need to have an
\family typewriter
id
\family default
attribute on the table and add the
\family typewriter
tablesorter
\family default
class to the table element.
Next you simply call the TableSorter widget from a snippet:
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
TableSorter Snippet
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
class TableSorterDemo {
\end_layout
\begin_layout Plain Layout
def render(xhtml: NodeSeq): NodeSeq = TableSorter("table-id")
\end_layout
\begin_layout Plain Layout
}
\end_layout
\begin_layout Plain Layout
\end_layout
\end_inset
\end_layout
\begin_layout Standard
The argument to TableSorter is the HTML element
\family typewriter
id
\family default
of the table you want sorted.
The TableSorter code relies on head merge (section
\begin_inset CommandInset ref
LatexCommand ref
reference "sec:Head-Merge"
\end_inset
) to put the appropriate JavaScript and jQuery functions into the returned
page.
\end_layout
\begin_layout Subsection
Calendar widgets
\begin_inset CommandInset label
LatexCommand label
name "sub:Calendar-widgets"
\end_inset
\end_layout
\begin_layout Standard
There are three calendar widgets corresponding to month, week and day views.
These widgets display calendars with a similar look and feel to Microsoft
Outlook or Google Calendar.They provide basic functionality for display,
but you can easily customize CSS and JavaScript hooks for calendar items
to fit your application requirements.
\end_layout
\begin_layout Paragraph
Calendar Month-View
\end_layout
\begin_layout Standard
\begin_inset Float figure
wide false
sideways false
status open
\begin_layout Plain Layout
\align center
\begin_inset Graphics
filename images/month-view.png
width 5in
\end_inset
\end_layout
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Calendar Month-View
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
\end_layout
\end_inset
\end_layout
\begin_layout Standard
This widget allows you to create month view calendars in your web page,
manage your calendar events etc.
The first thing you need to do is call the
\family typewriter
CalendarMonthView.init
\family default
function in your Boot class; this performs initialization by telling Lift's
\family typewriter
ResourceServer
\family default
about the paths to JavaScripts and stylesheets needed by this widget since
these dependencies are embedded in the same jar file (we'll cover this
topic more in section
\begin_inset CommandInset ref
LatexCommand ref
reference "sec:How-to-build-widgets"
\end_inset
).
\end_layout
\begin_layout Standard
The template for our widget example is relatively straightforward, as shown
in listing
\begin_inset CommandInset ref
LatexCommand ref
reference "lst:Month-view-template"
\end_inset
.
Basically, we provide a binding element where the calendar will be rendered.
\end_layout
\begin_layout Standard
\begin_inset listings
lstparams "language=XML"
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Month view template
\begin_inset CommandInset label
LatexCommand label
name "lst:Month-view-template"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
<lift:surround with="default" at="content">
\end_layout
\begin_layout Plain Layout
<h2>Calendar Month View Demo</h2>
\end_layout
\begin_layout Plain Layout
<lift:CalendarMonthViewDemo>
\end_layout
\begin_layout Plain Layout
<cal:widget/>
\end_layout
\begin_layout Plain Layout
</lift:CalendarMonthViewDemo>
\end_layout
\begin_layout Plain Layout
</lift:surround>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
In our snippet code, listing
\begin_inset CommandInset ref
LatexCommand ref
reference "lst:Month-view-snippet"
\end_inset
, we first perform some setup of the widget.
The Calendar widget takes a
\family typewriter
java.util.Calendar
\family default
instance telling it which month to display.
Additionally, it takes a Seq[CalendarItem] of items to be displayed on
the calendar.
Finally, it takes three arguments containing optional JavaScript functions
to be called when an item, day, or week is clicked, respectively.
In our example we're not showing any events or setting up any callbacks.
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Month view snippet
\begin_inset CommandInset label
LatexCommand label
name "lst:Month-view-snippet"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
class CalendarMonthViewDemo {
\end_layout
\begin_layout Plain Layout
def render(html: Group) : NodeSeq = {
\end_layout
\begin_layout Plain Layout
val c = Calendar.getInstance;
\end_layout
\begin_layout Plain Layout
c.set(MONTH, 0)
\end_layout
\begin_layout Plain Layout
bind("cal", html,
\end_layout
\begin_layout Plain Layout
"widget" -> CalendarMonthView(c, Nil, Empty, Empty, Empty)
\end_layout
\begin_layout Plain Layout
)
\end_layout
\begin_layout Plain Layout
}
\end_layout
\end_inset
\end_layout
\begin_layout Standard
In addition, CalendarMonthView can also take a MonthViewMeta instance as
the second argument so that you can control the first day of the week and
the locale used for formatting dates and times.
For instance, we could set the calendar to use Monday as the first day
of the week:
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
"widget" -> CalendarMonthView(c,
\end_layout
\begin_layout Plain Layout
MonthViewMeta(Calendar.MONDAY, Locale.getDefault),
\end_layout
\begin_layout Plain Layout
Nil, Empty, Empty, Empty)
\end_layout
\end_inset
\end_layout
\begin_layout Standard
Of course, without anything to display or do this isn't very useful, so
let's look at how you create CalendarItems.
\end_layout
\begin_layout Standard
Listing
\begin_inset CommandInset ref
LatexCommand ref
reference "lst:CalendarItem-example"
\end_inset
shows how we can create a calendar item for a meeting on June 5th at 2:30
pm.
We have to set up another Calendar instance to hold the time of the meeting,
then we use the CalendarItem helper object to set up the actual item instance.
The first parameter is the id of the div that will be created for the item.
This can be used from other scripts if needed.
The second argument is the time of the event.
The third argument is the CalendarType of the event, in this case, a meeting.
The optional method on CalendarItem allows you to set optional attributes
essentially via a sequence of
\begin_inset Formula $(CalendarItem)\Rightarrow CalendarItem$
\end_inset
functions.
This technique is used since CalendarItems are immutable and modifying
them returns new instances.
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
CalendarItem example
\begin_inset CommandInset label
LatexCommand label
name "lst:CalendarItem-example"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
val time = Calendar.getInstance
\end_layout
\begin_layout Plain Layout
time.setTime(DateFormat.pars("2009-06-05 2:30pm"))
\end_layout
\begin_layout Plain Layout
val meeting = CalendarItem("4", time, CalendarType.MEETING) optional (
\end_layout
\begin_layout Plain Layout
_ end(time),
\end_layout
\begin_layout Plain Layout
_ subject("Important Meeting!"))
\end_layout
\end_inset
\end_layout
\begin_layout Standard
The widget renders not only the XHTML to display the calendar, but it generates
the
\family typewriter
<script>
\family default
and CSS tags using head merge to control display.
One common customization of the widget would be to override the CSS used;
to do this, provide your own
\family typewriter
style.css
\family default
file under the WEB-INF/classes/calendars/monthview directory in your project.
Because Lift uses the classpath to load resources, your style.css file will
be
\begin_inset Quotes eld
\end_inset
found
\begin_inset Quotes erd
\end_inset
before the default one bundled in the lift-widgets jar file.
You can use the default
\family typewriter
style.css
\family default
as a starting point
\begin_inset Foot
status open
\begin_layout Plain Layout
\begin_inset CommandInset href
LatexCommand href
target "http://github.com/dpp/liftweb/tree/master/lift-widgets/src/main/resources/toserve/calendars/monthview/style.css"
\end_inset
\end_layout
\end_inset
.
\end_layout
\begin_layout Standard
The final thing we'd like to cover for the Month view is the JavaScript
callbacks.
These callbacks are constructed using the AnonFunc JavaScript artifact,
which essentially constructs an anonymous function on the client side.
Listing
\begin_inset CommandInset ref
LatexCommand ref
reference "lst:Calendar-callback-example"
\end_inset
shows an example of using the callbacks to redirect to an event view page
for the given event when the item is clicked.
In this example we assume that the id of each calendar item is its unique
id in the ORM (section
\begin_inset CommandInset ref
LatexCommand ref
reference "sub:Object-Relationships"
\end_inset
) and that we have a rewrite rule set up to handle item viewing (section
\begin_inset CommandInset ref
LatexCommand ref
reference "sec:URL-Rewriting"
\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
Calendar callback example
\begin_inset CommandInset label
LatexCommand label
name "lst:Calendar-callback-example"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
import JsCmds._
\end_layout
\begin_layout Plain Layout
val itemClick = Full(
\end_layout
\begin_layout Plain Layout
AnonFunc("elem, param", JsRaw("alert(elem);")
\end_layout
\end_inset
\end_layout
\begin_layout Paragraph
Calendar Week-View
\end_layout
\begin_layout Standard
\begin_inset Float figure
wide false
sideways false
status open
\begin_layout Plain Layout
\align center
\begin_inset Graphics
filename images/week-view.png
width 5in
\end_inset
\end_layout
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Calendar Week-View
\end_layout
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Standard
The CalendarWeekView widget provides a weekly view of the calendar.
The same general principles apply as for month view.
Again, you need to initialize the CalendarWeekView by calling the
\family typewriter
CalendarWeekView.init
\family default
function in your Boot class.
\end_layout
\begin_layout Standard
Listing
\begin_inset CommandInset ref
LatexCommand ref
reference "lst:Week-View-example"
\end_inset
shows a snippet returning a week view.
As you can see, we still use a Calendar instance to set the time, and we
also provide a WeekViewMeta in this example to set the first day of the
week and the locale.
The
\family typewriter
list
\family default
argument is a Seq[CalendarItem], constructed exactly the same as for a
month view.
Finally, we provide a JavaScript item callback.
Note that there aren't day or week callbacks available.
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Week view example
\begin_inset CommandInset label
LatexCommand label
name "lst:Week-View-example"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
class CalendarWeekViewDemo {
\end_layout
\begin_layout Plain Layout
def render(html: Group) : NodeSeq = {
\end_layout
\begin_layout Plain Layout
val c = Calendar.getInstance
\end_layout
\begin_layout Plain Layout
c.set(DAY_OF_MONTH, 17)
\end_layout
\begin_layout Plain Layout
c.set(MONTH, 4)
\end_layout
\begin_layout Plain Layout
bind("cal", html,
\end_layout
\begin_layout Plain Layout
"widget" -> CalendarWeekView(c,
\end_layout
\begin_layout Plain Layout
WeekViewMeta(MONDAY, Locale.getDefault()),
\end_layout
\begin_layout Plain Layout
list,
\end_layout
\begin_layout Plain Layout
itemClick))
\end_layout
\begin_layout Plain Layout
}
\end_layout
\begin_layout Plain Layout
}
\end_layout
\end_inset
\end_layout
\begin_layout Paragraph
Calendar Day-View
\end_layout
\begin_layout Standard
\begin_inset Float figure
wide false
sideways false
status open
\begin_layout Plain Layout
\align center
\begin_inset Graphics
filename images/day-view.png
width 5in
\end_inset
\end_layout
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Calendar Day-View
\end_layout
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Standard
The CalendarDayView widget renders a calendar for a single day.
The usage is essentially the same as for the month and week views, as shown
in listing
\begin_inset CommandInset ref
LatexCommand ref
reference "lst:Day-view-example"
\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
Day view example
\begin_inset CommandInset label
LatexCommand label
name "lst:Day-view-example"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
class CalendarDayViewDemo {
\end_layout
\begin_layout Plain Layout
def render(html: Group) : NodeSeq = {
\end_layout
\begin_layout Plain Layout
val c = Calendar.getInstance
\end_layout
\begin_layout Plain Layout
c.set(DAY_OF_MONTH, 17)
\end_layout
\begin_layout Plain Layout
c.set(MONTH, 4)
\end_layout
\begin_layout Plain Layout
bind("cal", html,
\end_layout
\begin_layout Plain Layout
"widget" -> CalendarDayView(c,
\end_layout
\begin_layout Plain Layout
DayViewMeta(Locale.getDefault()),
\end_layout
\begin_layout Plain Layout
list, itemClick)
\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 parameters are essentially the same, except that the Calendar object
represents the day that we want to render and we pass a DayViewMeta containing
just the Locale for internationalization purposes.
Again, only an item click callback is available.
\end_layout
\begin_layout Subsection
RSS Feed widget
\end_layout
\begin_layout Standard
\begin_inset Float figure
placement H
wide false
sideways false
status open
\begin_layout Plain Layout
\align center
\begin_inset Graphics
filename images/rssfeed.png
width 4in
\end_inset
\end_layout
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
RSSFeed widget
\end_layout
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Standard
The RSS feed widget, like its name implies, simply renders RSS feeds.
This widget does not need initialization in Boot since it has no dependencies
on JavaScript, CSS, etc.
In your snippet you simply use the RSSFeed helper object with the RSS feed
URL:
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
RSSFeed example
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
class RSSFeedDemo {
\end_layout
\begin_layout Plain Layout
def render(xhtml: NodeSeq): NodeSeq = {
\end_layout
\begin_layout Plain Layout
RSSFeed("http://www.praytothemachine.com/evil/index.php/feed/")
\end_layout
\begin_layout Plain Layout
}
\end_layout
\begin_layout Plain Layout
}
\end_layout
\end_inset
\end_layout
\begin_layout Standard
Although the RSSFeed widget doesn't provide its own CSS, the generated elements
do have CSS classes attached to them that you can provide styling for:
\end_layout
\begin_layout Description
rsswidget This class is attached to the outer div that contains all of the
feed elements
\end_layout
\begin_layout Description
rsswidgettitle This class is attached to the <li> that holds the title of
the feed
\end_layout
\begin_layout Description
rsswidgetitem This class is attached to each <li> element that holds an
RSS item
\end_layout
\begin_layout Subsection
Gravatar widget
\end_layout
\begin_layout Standard
Gravatars are
\series bold
g
\series default
lobally
\series bold
r
\series default
ecognized
\series bold
avatars
\series default
\begin_inset Foot
status open
\begin_layout Plain Layout
\begin_inset CommandInset href
LatexCommand href
target "http://gravatar.com"
\end_inset
\end_layout
\end_inset
.
You can add your picture at the Gravatar website and associate it with
one or more email addresses.
Sites that interact with Gravatar can fetch your picture and display it,
which is what the Gravatar widget does.
Listing
\begin_inset CommandInset ref
LatexCommand ref
reference "lst:Gravatar-example"
\end_inset
shows an example snippet that will render the Gravatar for the currentUser
into a
\family typewriter
<div>
\family default
, if available.
The default size of the Gravatar is 42x42 pixels, but you can override
this with additional parameters on the Gravatar.apply method.
Additionally, you can filter the Gravatar based on its rating (the default
rating is
\begin_inset Quotes eld
\end_inset
G
\begin_inset Quotes erd
\end_inset
only).
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Gravatar example
\begin_inset CommandInset label
LatexCommand label
name "lst:Gravatar-example"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
class GravatarDemo {
\end_layout
\begin_layout Plain Layout
def render(xhtml: NodeSeq) :NodeSeq = {
\end_layout
\begin_layout Plain Layout
Gravatar(currentUser.email)
\end_layout
\begin_layout Plain Layout
}
\end_layout
\begin_layout Plain Layout
}
\end_layout
\end_inset
\end_layout
\begin_layout Subsection
TreeView widget
\end_layout
\begin_layout Standard
The TreeView widget transforms an unordered list (
\family typewriter
<ul>
\family default
) into a tree-like structure using the TreeView JQuery plugin
\begin_inset Foot
status open
\begin_layout Plain Layout
\begin_inset CommandInset href
LatexCommand href
target "http://docs.jquery.com/Plugins/Treeview"
\end_inset
\end_layout
\end_inset
.
Each nested unordered list gets decorated with a +/- sign that allows you
to collapse or expand the entire sublist, as shown in figure
\begin_inset CommandInset ref
LatexCommand ref
reference "fig:TreeView-widget"
\end_inset
.
\end_layout
\begin_layout Standard
\begin_inset Float figure
wide false
sideways false
status open
\begin_layout Plain Layout
\align center
\begin_inset Graphics
filename images/treeview.png
width 3.5in
\end_inset
\end_layout
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
TreeView widget
\begin_inset CommandInset label
LatexCommand label
name "fig:TreeView-widget"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
\end_layout
\end_inset
\end_layout
\begin_layout Standard
To use this widget you first need to initialize the widget by calling the
\family typewriter
TreeView.init
\family default
function in your Boot class.
For basic usage, your snippet looks like listing
\begin_inset CommandInset ref
LatexCommand ref
reference "lst:TreeView-snippet"
\end_inset
.
The first argument is the id of the unordered list that you want transformed
into a tree.
The second argument is a JSON object that is used to configure the tree
view.
In our example, we're setting the treeview to animate opening and closing
of nodes with a 90 millisecond delay; for more options see the treeview
jQuery documentation page.
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
TreeView snippet
\begin_inset CommandInset label
LatexCommand label
name "lst:TreeView-snippet"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
class TreeViewDemo {
\end_layout
\begin_layout Plain Layout
def render(xhtml: Group): NodeSeq = {
\end_layout
\begin_layout Plain Layout
TreeView("example", JsObj(("animated" -> 90)))
\end_layout
\begin_layout Plain Layout
}
\end_layout
\begin_layout Plain Layout
}
\end_layout
\end_inset
\end_layout
\begin_layout Standard
In addition to transforming static lists into trees, the TreeView widget
also supports asynchronous loading of trees and nodes via Ajax calls.
In order to do this, you still need to provide an empty
\family typewriter
<ul>
\family default
element with an id attribute; this is essentially modified in place as
portions of the tree are loaded.
Next, you provide two functions that are used to retrieve the Tree data:
\end_layout
\begin_layout Enumerate
A function
\begin_inset Formula $()\Rightarrow List[Tree]$
\end_inset
to load the initial view of the tree.
This is what will be displayed to the client when the page loads, so if
you want some nodes to be available without having to make an Ajax call
this is where you define it.We will explain the Tree class in a moment.
\end_layout
\begin_layout Enumerate
A function
\begin_inset Formula $(String)\Rightarrow List[Tree]$
\end_inset
to load the children of a given node (the String argument is the node's
id)
\end_layout
\begin_layout Standard
The Tree class defines each node in the tree and contains several values
that define the appearance and behavior of the node:
\end_layout
\begin_layout Description
text The text to be displayed in the list item.
\end_layout
\begin_layout Description
id The optional HTML id of the element
\end_layout
\begin_layout Description
classes An optional string defining CSS classes to be assigned to the element
\end_layout
\begin_layout Description
expanded A boolean controlling whether the element will be expanded initially
(only valid if the haschildren is true or if the children list is populated)
\end_layout
\begin_layout Description
hasChildren If this is set to true but the children value is Nil, then the
TreeView widget will dynamically load the children of this node as described
in item #2 above
\end_layout
\begin_layout Description
children A List[Tree] defining the children of this element.
Setting this value will prevent Ajax from being used to retrieve the list
of children from the server on expansion
\end_layout
\begin_layout Standard
The Tree companion object has a number of overloaded apply methods that
make it easy to set one or more of these values without having to set all
of them.
\end_layout
\begin_layout Standard
To provide a concrete example, listing
\begin_inset CommandInset ref
LatexCommand ref
reference "lst:Tree-example"
\end_inset
shows implementations of the loadTree and loadNode functions corresponding
to the two Ajax functions used to dynamically construct the tree.
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Tree example
\begin_inset CommandInset label
LatexCommand label
name "lst:Tree-example"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
def loadTree () = {
\end_layout
\begin_layout Plain Layout
Tree(
"
No children
"
) ::
\end_layout
\begin_layout Plain Layout
Tree(
"
One static child
"
, Tree(
"
Lone child
"
) :: Nil) ::
\end_layout
\begin_layout Plain Layout
Tree(
"
Dynamic node
"
,
"
myDynamic
"
, true) :: Nil
\end_layout
\begin_layout Plain Layout
}
\end_layout
\begin_layout Plain Layout
\end_layout
\begin_layout Plain Layout
def loadNode (id : String) : List[Tree] = id match {
\end_layout
\begin_layout Plain Layout
case
"
myDynamic
"
=>
\end_layout
\begin_layout Plain Layout
Tree(
"
Child one
"
) ::
\end_layout
\begin_layout Plain Layout
Tree(
"
Child two
"
) :: Nil
\end_layout
\begin_layout Plain Layout
case _ => Nil
\end_layout
\begin_layout Plain Layout
}
\end_layout
\end_inset
\end_layout
\begin_layout Standard
In this example the initial view will show three nodes; the third node (
\begin_inset Quotes eld
\end_inset
Dynamic node
\begin_inset Quotes erd
\end_inset
) will fetch its children via an Ajax call when expanded.
The
\family typewriter
loadNode
\family default
method will then handle this call by adding two static leaf nodes to the
tree.
\end_layout
\begin_layout Subsection
Sparklines widget
\end_layout
\begin_layout Standard
The Sparklines widget is based on Will Larson's excellent Sparklines JavaScript
library
\begin_inset Foot
status open
\begin_layout Plain Layout
\begin_inset CommandInset href
LatexCommand href
target "http://www.willarson.com/code/sparklines/sparklines.html"
\end_inset
\end_layout
\end_inset
.
Sparklines are essentially small, high resolution charts embedded in text
that provide a wealth of information in a compact representation
\begin_inset Foot
status collapsed
\begin_layout Plain Layout
The term
\begin_inset Quotes eld
\end_inset
Sparkline
\begin_inset Quotes erd
\end_inset
was introduced by Edward Tufte in his book
\emph on
Beautiful Evidence
\emph default
.
Dr.
Tufte's work is a must read for anyone who si working with visualizing
large volumes of data.
\end_layout
\end_inset
.
\end_layout
\begin_layout Standard
\begin_inset Float figure
wide false
sideways false
status open
\begin_layout Plain Layout
\align center
\begin_inset Graphics
filename images/sparklines.png
width 3.5in
\end_inset
\end_layout
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Sparklines bar chart
\begin_inset CommandInset label
LatexCommand label
name "fig:Sparklines-bar-chart"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
\end_layout
\end_inset
\end_layout
\begin_layout Standard
As with our other widgets, you need to initialize the widget in your Boot
class by calling
\family typewriter
Sparklines.init
\family default
.
Listing
\begin_inset CommandInset ref
LatexCommand ref
reference "lst:Sparklines-snippet"
\end_inset
shows a simple snippet utilizing the widget to produce the graph shown
in figure
\begin_inset CommandInset ref
LatexCommand ref
reference "fig:Sparklines-bar-chart"
\end_inset
.
In your template you need to provide a canvas element with an
\family typewriter
id
\family default
attribute that will be used by the widget for its content.
In our example we provide a JsArray (an abstracted JavaScript array) with
our data, as well as a JSON object containing options for the chart
\begin_inset Foot
status collapsed
\begin_layout Plain Layout
More options can be found on Will Larson's Sparklines web page
\end_layout
\end_inset
.
We've set our options to draw percentage lines for the bar chart as well
as filling in the area between the percentage lines.
Finally, we call the Sparklines.onLoad method to generate the chart drawing
code (the chart will be drawn when the page is loaded).
The Sparklines library currently handles bar and line charts, which are
chosen via the SparklineStyle enumeration.
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Sparklines snippet
\begin_inset CommandInset label
LatexCommand label
name "lst:Sparklines-snippet"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
class SparklinesDemo {
\end_layout
\begin_layout Plain Layout
def render(html: NodeSeq): NodeSeq = {
\end_layout
\begin_layout Plain Layout
val data = JsArray(100,500,300,200,400,500,400,400,
\end_layout
\begin_layout Plain Layout
100,200, 345, 412, 111, 234, 490);
\end_layout
\begin_layout Plain Layout
val opts = JsObj(("percentage_lines" -> JsArray(0.5, 0.75)),
\end_layout
\begin_layout Plain Layout
("fill_between_percentage_lines" -> true),
\end_layout
\begin_layout Plain Layout
("extend_markings" -> false));
\end_layout
\begin_layout Plain Layout
Sparklines.onLoad("bar", SparklineStyle.BAR, data, opts);
\end_layout
\begin_layout Plain Layout
}
\end_layout
\begin_layout Plain Layout
}
\end_layout
\end_inset
\end_layout
\begin_layout Section
How to build a widget
\begin_inset CommandInset label
LatexCommand label
name "sec:How-to-build-widgets"
\end_inset
\end_layout
\begin_layout Standard
As we explained in the introduction, there is no magic formula when building
a widget since Lift and Scala provide so much base functionality without
having to resort to restrictions like traits or static XML binding.
However, there are a few items to note if you want to design your own widgets
\end_layout
\begin_layout Standard
Generally it's useful to make your widget a self-contained JAR file to simplify
dependency management and deployment.
Including things like style sheets and javascript libraries in your package
is quite straightforward if you're using Maven, but the question then becomes
how do you access these resources from a Lift application.
Fortunately, Lift provides some very simple mechanisms for using class
loaders to retrieve resources.
The basic functionality is handled through the
\family typewriter
ResourceServer
\family default
object
\begin_inset Foot
status open
\begin_layout Plain Layout
net.liftweb.http.ResourceServer
\end_layout
\end_inset
, which we cover in detail in section
\begin_inset CommandInset ref
LatexCommand ref
reference "sub:ResourceServer"
\end_inset
.
This object controls resource loading, and in particular handles where
resources can be loaded from.
Listing
\begin_inset CommandInset ref
LatexCommand ref
reference "lst:Adding-ResourceServer-permissions"
\end_inset
shows an example init method (similar to those that we've previously used
for the existing widgets) that tells the ResourceServer that it can load
resources from the path
\begin_inset Quotes eld
\end_inset
/classpath/mywidget
\begin_inset Quotes erd
\end_inset
.
You would locate these resources under the
\family typewriter
mywidget
\family default
package in your widget project.
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Adding ResourceServer permissions
\begin_inset CommandInset label
LatexCommand label
name "lst:Adding-ResourceServer-permissions"
\end_inset
\end_layout
\end_inset
import _root_.net.liftweb.http.ResourceServer
\end_layout
\begin_layout Plain Layout
def init() {
\end_layout
\begin_layout Plain Layout
ResourceServer.allow{
\end_layout
\begin_layout Plain Layout
case "iframewidget" :: _ => true
\end_layout
\begin_layout Plain Layout
}
\end_layout
\begin_layout Plain Layout
}
\end_layout
\end_inset
\end_layout
\begin_layout Standard
Once you've set up the appropriate permissions, your widget can generate
links or scripts that load from within the classpath, as shown in listing
\begin_inset CommandInset ref
LatexCommand ref
reference "lst:Sample-widget-rendering"
\end_inset
.
In this example we've defined a simple (and slightly ridiculous) widget
that renders a given URL into an IFrame element.
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Sample widget rendering
\begin_inset CommandInset label
LatexCommand label
name "lst:Sample-widget-rendering"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
class IFrameWidget {
\end_layout
\begin_layout Plain Layout
def render(url : String) =
\end_layout
\begin_layout Plain Layout
<head>
\end_layout
\begin_layout Plain Layout
<link type="text/css" rel=
"
stylesheet
"
\end_layout
\begin_layout Plain Layout
href={LiftRules.resourceServerPath + "/iframewidget/style.css"/>
\end_layout
\begin_layout Plain Layout
</head>
\end_layout
\begin_layout Plain Layout
<div class=
"
iframeDiv
"
>
\end_layout
\begin_layout Plain Layout
<iframe src={url}>
\end_layout
\begin_layout Plain Layout
<p>Your browser doesn't support IFrames</p>
\end_layout
\begin_layout Plain Layout
</iframe>
\end_layout
\begin_layout Plain Layout
</div>
\end_layout
\begin_layout Plain Layout
}
\end_layout
\end_inset
\end_layout
\begin_layout Standard
Note the path that we used uses the
\family typewriter
LiftRules.resourceServerPath
\family default
variable.
It's preferable to use this mechanism instead of hardcoding
\begin_inset Quotes eld
\end_inset
/classpath
\begin_inset Quotes erd
\end_inset
to allow for end-user flexibility.
We also use head merge to make sure the proper stylesheet is loaded for
the page.
\end_layout
\begin_layout Standard
As you can see, defining your own widget is not much different than writing
a snippet.
The major difference is in making resources accessible while bundling and
making sure that you avoid hardcoding properties that are configurable
by the end-users of your widget.
\end_layout
\end_body
\end_document