Overview
LiftWeb makes building dynamic websites extremely easy whilst hiding away a lot of the plumbing. RxScala is a Scala adaptor for RxJava, “a library for composing asynchronous and event-based programs using observable sequences for the Java VM”.
This blog describes how we combined Lift and RxScala for event-based UI components consuming and producing observable sequences.
The original motivation for combining Rx and Lift was simply as an experiment - could we treat UI components as sources and sinks of Observable streams of values.
The idea proved to be quite successful, particularly when the backend system is built with Rx so there is no impedance mismatch. We ended up using these ideas for a greenfield project in a large financial institution in London.
To try all the examples in this blog, clone RxLift, and assuming you have SBT installed, type the following on the command line at the root of the project:
Finally, point your browser at http://127.0.0.1:8080 to try out the examples.
The Basic Ideas
There are several fundamental ideas illustrated in the following figure:
- SHtml is Lift’s library for building UI components on which RxElement is built
- an Observable stream of values,
Obs[T]
, is mapped to Lift’s JavaScript abstraction,JsCmd
, and pushed via Lift’s Comet support to the client which updates elements on the client, using Lift’s JavaScript library - AJAX updates from the client are mapped into an Observable stream of values
The are two fundamental data types:
RxComponent wraps a function that accepts an Observable[T]
and returns an RxElement[O]
. It will become clear why this is necessary later but for now think of it as a function for building an RxElement
given an Observable
.
RxElement.values
is the output stream of values, the jscmd
is the stream of Lift JsCmds
containing JavaScript to send to the browser to make whatever changes are required in response to the input stream, and ui
is the HTML to bind into templates as usual in Lift.
To send the JsCmds emitted by RxElement.jscmd
to the browser, each JsCmd
needs to be sent to a comet actor that forwards it to the client.
RxLift’s RxCometActor
wraps up the mechanics of sending JavaScript to the client and managing subscription to the observables where necessary. Here is the code which should help your understanding of what is to follow.
A Label
The simplest example to start with is a text label since it maps an Observable[String]
to a stream of values pushed to the browser.
That’s it! The two lines of interest are the construction of timeLabel
and the call to publish
, the rest is vanilla RxScala or Lift. Components
is a collection of reusable UI components I’ve supplied, and publish is a method available via RxComentActor
.
An Input Element
The label above doesn’t emit anything so here is an example of an input element, whose values are emitted as an Observable[String]
.
To get values from the input field, subscribe to in.values
which is an Observable[String]
.
Composite Elements
So far we can build UIs for simple streams of values. How can we build a reusable UI component for an Observable
of some richer structure?
The solution we opted for was to use Scalaz Lens
. The input Observable
is mapped to Observables
for each field with a set of lenses. But the complication is how to apply values emitted by each field’s component to the original data. The set of Observable
values need to be combined in some way to effect a change on the original value.
We addressed this by mapping each field’s Observable
to an Observable[[Endo][T]]
, where T
is the type of the composite datatype (an Endo
wraps a function of T => T
). The set of Observable[Endo[T]]
for each field can be merged and applied to the original datatype.
The resulting component’s type is RxComponent[T, Endo[T]]
.
I think this is one of those cases where code speaks louder than explanations.
Here is a model and an RxComponent
for the model.
The interesting code here is the focus method from RxLift’s Components.scala
. It applies a Lens[T, F]
to an RxComponent[F, F]
producing an RxComponent[T, Endo[T]]
, which brings us back to why RxComponent
is needed.
An RxElement
is the result of applying an Observable
to an RxComponent
, so it is not possible to modify its input after construction. RxComponents
allow us to map and transform Observables
before their RxElement
is produced.
The last line, fn + ln
, combines each field into a RxComponent[Person, Endo[Person]]
by merging the Observable
streams of fn
and ln
, and joining the UI NodeSeqs
.
Consequently, a change in any field will result in an Endo[T]
being emitted and so multiple updates to a datatype by different users will be safe. One user’s update will not overwrite anothers unless the same field is modified simultaneously by two or more users.
Here is a UI that uses the component.
(The BehaviourSubject
is a subject that emits the most recent item it has observed and all subsequent observed items to each subscribed Observer
.)
Conclusions
Building reactive UI components based on RxScala and Lift has proven to be fairly straightforward. RxLift is an example project illustrating the basic ideas which have been used to build a fairly sophisticated UI in a large financial institution in London.