Making UI easily with Binding.scala

Making UI easily with Binding.scala

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.

Why Binding.scala?

There are many one-way data-binding (or “reactive”) libraries nowadays. In the JavaScript world, a well-known one is React, while in the Scala world, there’s Scala.Rx. Why would you want to use Binding.scala, then? There are many reasons, including:

  • 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.

Hello, World!

Let’s start with a minimal example (ScalaFiddle):

https://gist.github.com/anonymous/75cdb6c36e2d97cee3dcce1083b6a4bd

The above code should print:

https://gist.github.com/anonymous/78a1ab7de89124c631c372f5e43e18fd

You’re probably already starting to grasp the idea behind this code, but we’ll analyze it in detail now, just in case.

Step-by-step analysis

https://gist.github.com/anonymous/e90503b3ddec2eb05da232b66200bc41

Here, user is a Var[String] and it’s being initialized with the value "Anonymous"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.

https://gist.github.com/anonymous/0f583d1a27a54f1557f802f9eb959745

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 Binding[Unit] (because println return type is Unit).

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:

https://gist.github.com/anonymous/3f50c027d95b1212b1d3543f01e18ab7

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:

https://gist.github.com/anonymous/f8b03e79d3bfee5a8ce07139789125f3

we change the value in user to "Alice", which triggers re-evaluation of helloPrinter, resulting in:

https://gist.github.com/anonymous/83af1759d6736aafb3ba8c7856630765

printed on the standard output. Then, we do:

https://gist.github.com/anonymous/b90a073a7ed1c9433a12d6cf746ad4b2

and it prints:

https://gist.github.com/anonymous/02ba29bb4e5ed9d423eaeed1c04b7273

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 Bindings.

Var is a Binding too

OK, not very useful so far, I know. The real power of Binding.scala comes from the fact that you can easily compose Bindings:

https://gist.github.com/anonymous/3bbddde238a404ccfcc7dc4978f086b3

Output:

https://gist.github.com/anonymous/a3be6db57418f4b89fe775fdbf0573a9

As you can see, we can use the value of one Binding (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 width or 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 width and 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 Ints and Strings as Vars:

https://gist.github.com/anonymous/02ac1fd91c61e2f381ae75a6a595578a

Output:

https://gist.github.com/anonymous/5779d7a28daf9858f4c68e2b5db5dceb

Great! We can also make the code a bit more functional, if we want:

https://gist.github.com/anonymous/0ca4d30911f6101d70053d2f6ac86d57

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 Var[Rectangle].

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:

https://gist.github.com/anonymous/c79b062a9b5c732fe70aba5e0485ef41

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:

binding scala example

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

First, we need to create a Scala.js project. You can follow the official documentation, or use this Giter8 template for Scala.js projects. Then, add the Binding.scala dependency to your build.sbt:

https://gist.github.com/anonymous/ef1252384454a4fd2a6c19280c2f2da4

The following imports will be needed:

https://gist.github.com/anonymous/a73251e720d2b1ceec28bc7a79346a64

The absolutely minimal HTML to test this is:

https://gist.github.com/anonymous/4db4ca0c7f8059ffe625acf391226180

Of course, you’ll need to customize the path to your target JavaScript file.

Model

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:

https://gist.github.com/anonymous/f83b9bbf48b76bb40e23bf11a84afb3b

We define some sample data:

https://gist.github.com/anonymous/2f859330f09c7710d9038f16d230a31b

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.

User Interface

We have the data, so it’s time to build the user interface:

https://gist.github.com/anonymous/08fa5f0f304b30e8fddb02cfec0c10b8

This function will render the main <div> containing our whole UI: input fields for amount and 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:

https://gist.github.com/anonymous/71919771f0d5cd51261363221e3b583b

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.

The 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 amount variable.

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:

https://gist.github.com/anonymous/2cb490d276920675fad657777a0d4fca

However, for such simple event handlers it’s easier to define them inline.

https://gist.github.com/anonymous/aad0b7169b91586b797fa84f09f79f31

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:

https://gist.github.com/anonymous/cd335ddbd9a589136c669aa425595b60

One of the useful methods provided by Vars is 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 _). Since renderDeposit is a @dom function itself, we need to bind its value afterwards.

https://gist.github.com/anonymous/020b28f779d2bd6e8203ee4d6b4ff5af

Nothing new here. We are just creating a row with three columns: periodinterest (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:

https://gist.github.com/anonymous/f8ec6185f2da213349e37b3dd413dbc2

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 document.

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.

Modifying Vars

To be able to add deposits, we need to make a simple form:

https://gist.github.com/anonymous/99e2422d30655807b8b690d437f175ca

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:

https://gist.github.com/anonymous/9224fbd04f949e6d81c7825b1421b984

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:

https://gist.github.com/anonymous/7c6888cbe9bac57f83aab23eca0407eb

Now we just need to adjust renderDepositsTable:

https://gist.github.com/anonymous/56f39e7e1dd5053be2e371a6e175b5da

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

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.

FutureBinding

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:

https://gist.github.com/anonymous/88dc10dc2d4aa2455f196197027e615b

You can wrap any Future with 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:

https://gist.github.com/anonymous/177f59dfe3241aa5e187324c6f0ebf4e

it will be using:

https://gist.github.com/anonymous/7000fabfc7a8f02010f182b6d8b9f0dc

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.

Conclusion

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…

Links

Do you like this post? Want to stay updated? Follow us on Twitter or subscribe to our Feed.

Download e-book:

Scalac Case Study Book

Download now

Authors

Paweł Bartkiewicz

I have a broad experience with programming languages and technologies, I choose Scala as my preferred tool nowadays. I take security very seriously due to my background in developing military-grade appliances. I am the best at back-end development, but I am not afraid of any programming-related tasks. I love types. In my spare time, I contribute to open source, play role-playing games (pen & paper), do some gardening with my wife, read Lovecraft, tinker with electronics, build synthesizer, sometimes brew beer.

Latest Blogposts

17.04.2024 / By  Michał Szajkowski

Mocking Libraries can be your doom

Test Automations

Test automation is great. Nowadays, it’s become a crucial part of basically any software development process. And at the unit test level it is often a necessity to mimic a foreign service or other dependencies you want to isolate from. So in such a case, using a mock library should be an obvious choice that […]

04.04.2024 / By  Aleksander Rainko

Scala 3 Data Transformation Library: ducktape 0.2.0.

Scala 3 Data Transformation Library: Ducktape 2.0

Introduction: Is ducktape still all duct tape under the hood? Or, why are macros so cool that I’m basically rewriting it for the third time? Before I go off talking about the insides of the library, let’s first touch base on what ducktape actually is, its Github page describes it as this: Automatic and customizable […]

28.03.2024 / By  Matylda Kamińska

Scalendar April 2024

scala conferences april 2024

Event-driven Newsletter Another month full of packed events, not only around Scala conferences in April 2024 but also Frontend Development, and Software Architecture—all set to give you a treasure trove of learning and networking opportunities. There’re online and real-world events that you can join in order to meet colleagues and experts from all over the […]

software product development

Need a successful project?

Estimate project