Making UI easily with Binding.scala
Binding.scala is a one-way data-binding library written in Scala. It lets you create reactive user interfaces by writing concise Scala code. In this post, I’ll guide you step by step through the basics, as well as creating a simple dynamic web page. On top of that, I will discuss some undocumented issues I faced, which might be interesting for you if you decide to give Binding.scala a chance in your next project.
- It’s written in Scala (obviously), so if you want to write your code in Scala as well, you don’t need to worry about compatibility
- Targets both JS and JVM, so you can use it in non-web projects too
- Supports statically type-checked HTML literals (not kidding!)
- Properly manages the life-cycle of allocated objects, so no memory leaks
- Uses precise data-binding instead of virtual DOM, so it’s potentially faster
- It’s easy to learn
The biggest downside I’ve found so far is that the documentation is somewhat lacking, and that’s why I decided to write this tutorial.
Let’s start with a minimal example (ScalaFiddle):
The above code should print:
You’re probably already starting to grasp the idea behind this code, but we’ll analyze it in detail now, just in case.
user is a
Var[String] and it’s being initialized with the value
Var is a wrapper which lets you change the value inside, so in practice you can think of
user as a
var user: String. “Ugh, mutability?!” you exclaimed in disgust, quite likely. Don’t worry, it’s a relatively safe and tame mutability, as we’ll see in a minute. Not the wild kind you know and fear, and rightfully so.
Binding, on the other hand, wraps some code which depends on some
Vars. In this case
helloPrinter depends on
user, which it concatenates with some
String literals and prints the result. So
helloPrinter is of type
println return type is
Have you noticed how we used
user.bind instead of
user directly? This is how you extract values from
Vars. But it doesn’t simply get the value out and return: it also informs the library that
helloPrinter depends on
user, which is very important, as we’ll see soon.
Up till now, the above code does nothing out of the ordinary. It simply prints
Hello, Anonymous!. However, here is where the magic begins:
This line informs Binding.scala that it should start watching for changes in the
Vars on which
helloPrinter depends. So each time
user changes value,
helloPrinter gets re-evaluated. Thus, when we do:
we change the value in
"Alice", which triggers re-evaluation of
helloPrinter, resulting in:
printed on the standard output. Then, we do:
and it prints:
The important thing to note here is that we must use
:= instead of
= to update
Vars and that it results in immediate re-evaluation of all dependent
Var is a
OK, not very useful so far, I know. The real power of Binding.scala comes from the fact that you can easily compose
As you can see, we can use the value of one
area in this case) when computing another (
areaPrinter). That’s because
bind is a method of trait
Binding, and yes,
Var is a
Binding as well.
Another thing you may have noticed is that
areaPrinter gets re-evaluated whenever we change either
height. This may cause a “glitch”: a temporary state inconsistency.
For example, we may want to calculate the area of a
5x4 rectangle first, then a
4x6 rectangle. However, since we update
height independently, the system will produce a state where the area of a
4x4 rectangle is shown, because
height hasn’t been updated yet.
How can we avoid such glitches? Some libraries let you suspend observers, do the updates and resume normal operation after you’re sure that the input state is consistent. But what to do if the library we chose has no such functionality, as in the case of Binding.scala? Well, no one said you can only use simple
Great! We can also make the code a bit more functional, if we want:
We just replaced the hardcoded
val area with a function taking the rectangle as a parameter and we did the same with
areaPrinter. You may be wondering why we specified
Binding[Rectangle] as the parameter type for
area, when our
rectangle is really a
Short answer: why not? We don’t intend to modify this
Var within this function. The only method we need is
bind, which is provided by the
Binding trait. This way, we could easily do the following, if we’d like:
However, there’s a more important reason: by not exposing
Var we ensure that no-one will modify the value from within another part of the code (not without type-casting, at least). If we enforce a rule that each
Var can be assigned only in a single place, we don’t risk losing our sanity.
OK, now that you know the basics, we can move on to creating some real user interface with Scala.js.
Binding.scala + Scala.js = ♥
The thing that distinguishes Binding.scala from other data-binding libraries is that it supports Scala’s XML literals, so you can dynamically build your HTML DOM within your Scala code very easily. We’ll explore this by creating a very simple web application for calculating time deposits.
We could stick to the tradition and implement yet another TodoMVC, but there’s already at least onewritten with Binding.scala. To make things even simpler, we won’t concern ourselves with things such as clean architecture, because that’s not the point of this article.
We will create this:
I used Materialize to make the page pretty, but in the code snippets within this article I omitted any appearance-specific things.
Don’t worry, there will be a link to the complete project at the end of this article.
Setting up the project
The following imports will be needed:
The absolutely minimal HTML to test this is:
Our business logic is very simple: we just take an
amount of money to invest, a
duration (in months) of the investment and a list of
Deposit types available (each characterized by
period in months and
interest rate), then we calculate the
gain for each deposit:
We define some sample data:
The first two lines should be pretty self-explanatory by now, but what is
Vars? It’s similar to
Var, but it holds a mutable list of values. We’ll see how to operate on this list in a moment.
We have the data, so it’s time to build the user interface:
This function will render the main
<div> containing our whole UI: input fields for
duration, as well as the table of
deposits. If you’re not familiar with XML literals in Scala, they’re almost normal XML with Scala code embedded between braces (
The most important thing to note here is that we aren’t wrapping the code with
Binding. Instead, we are using a
@dom macro annotation on the function, which does the same thing, but it also does some HTML-specific magic behind the scenes, like converting the XML to Scala.js DOM and making sure that only the relevant portion of the DOM gets updated when some
Binding changes value.
We’ll implement the
render* functions next:
Here we’re rendering the input field for
amount. Since Binding.scala is statically typed, we need the
toString: the attribute
value expects a
String, not a
Double. This may seem annoying, but may save you many hours of debugging in case you accidentally assign wrong value to an attribute.
onchange listener may be not obvious at a first glance. As all event listeners in the DOM, it has to be a function with a single parameter:
Event. It may return something (e.g.
false to stop event propagation), but it doesn’t have to. Here we’re ignoring the parameter and simply assign the value of the input field to the
But wait, where’s
amountInput coming from? Well, another “magical” thing that the
@dom macro does is exposing the DOM elements as local symbols named after their
id and having the proper type (
HTMLInputElement, in this case). Sounds like true magic, but it’s very handy when writing more complex user interfaces.
Of course, we could also define the function somewhere else and reference it here, like so:
However, for such simple event handlers it’s easier to define them inline.
The input field for
duration is very similar to the one for
amount, so we’ll skip the explanation.
Now, we want to display the table of deposits:
One of the useful methods provided by
map: it’s just like the normal
map you know from Scala collections such as
Seq. Here, it’s iterating the elements and calling
renderDeposit for each
Deposit (passed using the anonymous argument
renderDeposit is a
@dom function itself, we need to
bind its value afterwards.
Nothing new here. We are just creating a row with three columns:
interest (formatted as percentage) and the calculated
gain (formatted with two decimal places).
Finally, we bind the function rendering our main container to the element
#mainContainer from our HTML:
Instead of calling
renderMainContainer.watch() (which would do nothing, because
renderMainContainer has no side-effects) we’re using Binding.scala’s
dom.render function, which takes the DOM structure produced by
renderMainContent and automatically inserts it at the specified place in the
The application should be usable by now: you can change the
amount or the
duration and the
gains should re-calculate automatically. Easy, right? Let’s make it possible to add/remove
Deposit types now.
You can find the full source code we wrote so far in this commit.
To be able to add deposits, we need to make a simple form:
Again, it’s almost the same as with normal (mutable) collections: we use the operator
+= to add an element. However, we need to call
get on the
deposits first. Yes, it’s inconsistent with the
:= notation, but it’s been fixed in Binding.scala v11.0.0-M2. For now we have to live with this inconsistency (unless you’re from a future in which
11.0.0 got released already).
We’ll put this form at the end of the deposits table:
So far so good.
Removing deposits is a bit tricky, however: the
delete button should be rendered in
renderDeposit, but we can’t access
deposits there. We could, of course, just add
deposits: Vars[Deposit] to the function’s parameters, but it feels wrong. Why would a function rendering a single deposit need access to the list of all deposits?
A much cleaner solution is passing a callback:
Now we just need to adjust
I reformatted the
map call a little and added
onDelete as a named argument to make it more readable. The actual callback is very straightforward: we just use
-= instead of
+= this time, because we want to remove an element.
And that’s all: our application should be fully functional (i.e. working) now. Maybe not the cleanest code you saw in your life, but I wanted to include many different use cases, so that you can choose yourself what suits you best.
Links to the complete project
- GitHub repository
- Online demo
- Simpler version without Materialize
- ScalaFiddle – if you want to experiment with the code inside your browser
Things to keep in mind
Before you go (and start writing that awesome web app you wanted to do for a long time but felt the frontend is too much work), I’d like to mention a few other things you’ll probably encounter while using Binding.scala.
Yes, I know, every example should use some REST API. I skipped that not because it’s impossible or difficult with Binding.scala – on the contrary:
You can wrap any
FutureBinding, not only AJAX calls.
Changes in v11.0
As mentioned earlier, version 11.0 (not released yet at the time of writing this article) will deprecate the
:= operator and
get method for
Var, replacing them with
value for consistency. So instead of:
it will be using:
Beyond HTML DOM
Despite having a very good HTML support, Binding.scala isn’t limited to HTML. Right now, there’s also a support library for JavaFX’s FXML and nothing stops you from integrating Binding.scala with another framework/library of your choice.
We saw how easy Binding.scala is, especially compared to other data-binding libraries. It’s written in Scala, works with JVM and JS targets, has good support for HTML and avoids memory leaks. On the downside, it’s not as mature as some other libraries and it’s still lacking detailed documentation.
Would I use it for a large, serious project now? Probably not, but I hope it gets to that stage someday. Would I use it for hobby projects and prototypes? Definitely yes.
However, another interesting library showed up recently: monadic-html. It seems to combine most advantages of Binding.scala and Scala.rx, but that’s probably a topic for another post…
- GitHub repository for this example project
- Giter8 template for Scala.js projects
- More Binding.scala examples on ScalaFiddle