SkillAgentSearch skills...

Binding.scala

Reactive data-binding for Scala

Install / Use

/learn @ThoughtWorksInc/Binding.scala

README

Binding.scala <a href="http://thoughtworks.com/"><img align="right" src="https://www.thoughtworks.com/imgs/tw-logo.png" title="ThoughtWorks" height="15"/></a>

Production Ready Extremely Lightweight

Join the chat at https://gitter.im/ThoughtWorksInc/Binding.scala StackOverflow Scala CI Scaladoc Latest version

Binding.scala is a data-binding library for Scala, running on both JVM and Scala.js.

Binding.scala can be used as the basis of UI frameworks, however latest Binding.scala 12.x does not contain any build-in UI frameworks any more. For creating reactive HTML UI, you may want to check out html.scala, which is an UI framework based on Binding.scala, and it is also the successor of previously built-in dom library. See also React / Binding.scala / html.scala Interoperability for using existing React components with Binding.scala,

See Binding.scala • TodoMVC or ScalaFiddle DEMOs as examples for common tasks when working with Binding.scala.

Comparison to other reactive web frameworks

Binding.scala and html.scala has more features and less concepts than other reactive web frameworks like ReactJS.

<table> <thead> <tr> <th></th> <th>Binding.scala</th> <th>ReactJS</th> </tr> </thead> <tbody> <tr> <th>Support HTML literal?</th> <td>Yes</td> <td>Partially supported. Regular HTML does not compile, unless developers manually replaces <code>class</code> and <code>for</code> attributes to <code>className</code> and <code>htmlFor</code>, and manually converts inline <code>style</code>s from CSS syntax to JSON syntax.</td> </tr> <tr> <th>Algorithm to update DOM</th> <td>Precise data-binding, which is faster than virtual DOM</td> <td>Virtual DOM differentiation, which requires manually managed <code>key</code> attributes for complicated DOM.</td> </tr> <tr> <th>Lifecycle management for data-binding expressions</th> <td>Automatically</td> <td>N/A</td> </tr> <tr> <th>Statically type checking</th> <td>Yes, even for HTML tags and attribues</td> <td>No</td> </tr> <tr> <th>Learning curve</th> <td>Always easy</td> <td>Easy to start. Requires much more efforts to understand its corner cases.</td> </tr> </tbody> </table>

See Design section for more information.

Getting started

We will build an Binding.scala web page during the following steps.

Step 0: Setup a Sbt Scala.js project

See http://www.scala-js.org/tutorial/basic/ for information about how to setup such a project.

Step 1: Add html.scala dependencies into your build.sbt:

// Enable macro annotations by setting scalac flags for Scala 2.13
scalacOptions ++= {
  import Ordering.Implicits._
  if (VersionNumber(scalaVersion.value).numbers >= Seq(3L)) {
    Nil
  } if (VersionNumber(scalaVersion.value).numbers >= Seq(2L, 13L)) {
    Seq("-Ymacro-annotations")
  } else {
    Nil
  }
}

// Enable macro annotations by adding compiler plugins for Scala 2.12
libraryDependencies ++= {
  import Ordering.Implicits._
  if (VersionNumber(scalaVersion.value).numbers >= Seq(2L, 13L)) {
    Nil
  } else {
    Seq(compilerPlugin("org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full))
  }
}

libraryDependencies += "com.yang-bo" %%% "html" % "latest.release"

Step 2: Create a data field, which contains some Var and Vars as data source for your data-binding expressions

case class Contact(name: Var[String], email: Var[String])

val data = Vars.empty[Contact]

A Var represents a bindable variable, which also implements Binding trait, hence a Var can be seen as a binding expression as well. If another expression depends on a Var, the value of the expression changes whenever value of the Var changes.

A Vars represents a sequence of bindable variables, which also implements BindingSeq trait, hence a Vars can be seen as a binding expression of a sequence as well. If another comprehension expression depends on a Vars, the value of the expression changes whenever value of the Vars changes.

Step 3: Create a @html method that contains data-binding expressions

// For Scala 3
def table: Binding[HTMLTableElement] = {
  html"""<table border="1" cellPadding="5">
    <thead>
      <tr>
        <th>Name</th>
        <th>E-mail</th>
      </tr>
    </thead>
    <tbody>
      ${
        for (contact <- data) yield {
          html"""<tr>
            <td>
              ${contact.name.bind}
            </td>
            <td>
              ${contact.email.bind}
            </td>
          </tr>"""
        }
      }
    </tbody>
  </table>"""
}
// For Scala 2
@html
def table: Binding[HTMLTableElement] = {
  <table border="1" cellPadding="5">
    <thead>
      <tr>
        <th>Name</th>
        <th>E-mail</th>
      </tr>
    </thead>
    <tbody>
      {
        for (contact <- data) yield {
          <tr>
            <td>
              {contact.name.bind}
            </td>
            <td>
              {contact.email.bind}
            </td>
          </tr>
        }
      }
    </tbody>
  </table>
}

html"""...""" interpolation in Scala 3 (or @html annotated methods in Scala 3) represents an reactive XHTML template, which supports HTML literal. The type of HTML interpolation/literal is a specific subtype of com.thoughtworks.binding.Binding[org.scalajs.dom.Node] or com.thoughtworks.binding.Binding.BindingSeq[org.scalajs.dom.Node], instead of scala.xml.Node or scala.xml.NodeSeq. So we could have def node: Binding[HTMLBRElement] = html"""<br/>""" and def node: BindingSeq[HTMLBRElement] = html"""<br/><br/>""".

A HTML interpolation/literal method is composed with other data-binding expressions in two ways:

  1. You could use bind method in an interpolation to get the value of another Binding.
  2. You could use for / yield expression in a @html method to map a BindingSeq to another.

You can nest Node or BindingSeq[Node] in other HTML element literals via { ... } interpolation syntax.

Step 4: Render the data-binding expressions to DOM in the main method

@JSExport
def main(): Unit = {
  html.render(document.body, table)
}

Step 5: Invoke the main method in a HTML page

<!DOCTYPE html>
<html>
  <head>
    <script type="text/javascript" src="js-fastopt.js"></script>
  </head>
  <body>
    <script type="text/javascript">
      SampleMain().main()
    </script>
  </body>
</html>

Now you will see a table that just contains a header, because data is empty at the moment.

Step 6: Add some <button> to fill data for the table

def table: BindingSeq[Node] = {
  html"""<div>
    <button
      onclick=${ event: Event =>
        data.value += Contact(Var("Yang Bo"), Var("yang.bo@rea-group.com"))
      }
    >
      Add a contact
    </button>
  </div>
  <table border="1" cellPadding="5">
    <thead>
      <tr>
        <th>Name</th>
        <th>E-mail</th>
        <th>Operation</th>
      </tr>
    </thead>
    <tbody>
      ${
        for (contact <- data) yield {
          <tr>
            <td>
              ${contact.name.bind}
            </td>
            <td>
              ${contact.email.bind}
            </td>
            <td>
              <button
                onclick=${ event: Event =>
                  contact.name.value = "Modified Name"
                }
              >
                Modify the name
              </button>
            </td>
          </tr>
        }
      }
    </tbody>
  </table>"""
}

When you click the "Add a contact" button, it appends a new Contact into data, then, Binding.scala knows the relationship between DOM and data, so it decides to append a new <tr> corresponding to the newly appended Contact.

And when you click the "Modify the name", the name field on contact changes, then, Binding.scala decides to change the content of the corresponding tr to new value of name field.

Design

Precise data-binding

ReactJS requires users to provide a render function for each component. The render function should map props and state to a ReactJS's virtual DOM, then ReactJS framework creates a DOM with the same structure as the virtual DOM.

When state changes, ReactJS framework invokes render function to get a new vi

View on GitHub
GitHub Stars1.6k
CategoryDevelopment
Updated14d ago
Forks102

Languages

Scala

Security Score

100/100

Audited on Mar 18, 2026

No findings