Giter Club home page Giter Club logo

binding.scala's Introduction

Binding.scala

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.

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

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("[email protected]"))
      }
    >
      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 virtual DOM. Unfortunately, ReactJS framework does not precisely know what the state changing is. ReactJS framework has to compare the new virtual DOM and the original virtual DOM, and guess the changeset between the two virtual DOM, then apply the guessed changeset to the real DOM as well.

For example, after you prepend a table row <tr> into an existing <tbody> in a <table>, ReactJS may think you also changed the content of every existing <tr> of the <tbody>.

The reason for this is that the render function for ReactJS does not describe the relationship between state and DOM. Instead, it describes the process to create a virtual DOM. As a result, the render function does not provide any information about the purpose of the state changing, although a data-binding framework should need the information.

Unlike ReactJS, a Binding.scala @html method is NOT a regular function. It is a template that describes the relationship between data source and the DOM. When part of the data source changes, Binding.scala knows about the exact corresponding partial DOM affected by the change, thus only re-evaluating that part of the @html method to reflect the change in the DOM.

With the help of the ability of precise data-binding provided by Binding.scala, you can get rid of concepts for hinting ReactJS's guessing algorithm, like key attribute, shouldComponentUpdate method, componentDidUpdate method or componentWillUpdate method.

Composability

The smallest composable unit in ReactJS is a component. It is fair to say that a React component is lighter than an AngularJS controller, while Binding.scala is better than that.

The smallest composable unit in Binding.scala is a @html method. Every @html method is able to compose other @html methods via .bind.

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

def bindingButton(contact: Contact) = {
  html"""<button
    onclick=${ event: Event =>
      contact.name.value = "Modified Name"
    }
  >
   Modify the name
  </button>"""
}

def bindingTr(contact: Contact) = {
  html"""<tr>
    <td>${ contact.name.bind }</td>
    <td>${ contact.email.bind }</td>
    <td>${ bindingButton(contact).bind }</td>
  </tr>"""
}

def bindingTable(contacts: BindingSeq[Contact]) = {
  html"""<table>
    <tbody>
      ${
        for (contact <- contacts) yield {
          bindingTr(contact)
        }
      }
    </tbody>
  </table>"""
}

@JSExport
def main(): Unit = {
  val data = Vars(Contact(Var("Yang Bo"), Var("[email protected]")))
  dom.render(document.body, bindingTable(data))
}

You may find out this approach is much simpler than ReactJS, as:

  • Instead of passing props in ReactJS, you just simply provide parameters for Binding.scala.
  • Instead of specifying propTypes in ReactJS, you just simply define the types of parameters in Binding.scala.
  • Instead of raising a run-time error when types of props do not match in ReactJS, you just check the types at compile-time.

Lifecycle management for data-binding expressions

The ability of precise data-binding in Binding.scala requires listener registrations on the data source. Other reactive frameworks that have the ability ask users manage the lifecycle of data-binding expressions.

For example, MetaRx provides a dispose method to unregister the listeners created when building data-binding expressions. The users of MetaRx have the responsibility to call dispose method for every map and flatMap call after the expression changes, otherwise MetaRx leaks memory. Unfortunately, manually disposeing everything is too hard to be right for complicated binding expressions.

Another reactive web framework Widok did not provide any mechanism to manage lifecycle of of data-binding expressions. As a result, it simply always leaks memory.

In Binding.scala, unlike MetaRx or Widok, all data-binding expressions are pure functional, with no side-effects. Binding.scala does not register any listeners when users create individual expressions, thus users do not need to manually unregister listeners for a single expression like MetaRx.

Instead, Binding.scala creates all internal listeners together, when the user calls dom.render or Binding.watch on the root expression. Note that dom.render or Binding.watch manages listeners on all upstream expressions, not only the direct listeners of the root expression.

In brief, Binding.scala separates functionality in two kinds:

  • User-defined @html methods, which produce pure functional expressions with no side-effects.
  • Calls to dom.render or Binding.watch, which manage all side-effects automatically.

HTML literal and statically type checking

As you see, you can embed HTML literals in @html methods in Scala source files. You can also embed Scala expressions in braces in content or attribute values of the HTML literal.

def notificationBox(message: String): Binding[Div] = {
  html"""<div class="notification" title=${ s"Tooltip: $message" }>
    {
      message
    }
  </div>"""
}

Despite the similar syntax of HTML literal between Binding.scala and ReactJS, Binding.scala creates real DOM instead of ReactJS's virtual DOM.

In the above example, <div>...</div> creates a DOM element with the type of org.scalajs.dom.html.Div. Then, the magic @html lets the method wrap the result as a Binding.

You can even assign the HTMLDivElement to a local variable and invoke native DOM methods on the variable:

def notificationBox(message: String): Binding[HTMLDivElement] = {
  val result: Binding.Stable[HTMLDivElement] = html"""<div class="notification" title=${ s"Tooltip: $message" }>
    ${
      message
    }
  </div>"""

  result.value.scrollIntoView()

  result
}

scrollIntoView method will be invoked when the HTMLDivElement is created. If you invoke another method not defined in HTMLDivElement, the Scala compiler will report a compile-time error instead of bringing the failure to run-time, because Scala is a statically typed language and the Scala compiler understands the type of Div.

You may also notice class and title. They are DOM properties or HTML attributes on Div. They are type-checked by Scala compiler as well.

For example, given the following typo method:

def typo = {
  val myDiv = html"""<div typoProperty="xx">content</div>"""
  myDiv.value.typoMethod()
  myDiv
}

The Scala compiler will report errors like this:

typo.scala:23: typoProperty is neither a valid property nor a valid attribute for <DIV>
        val myDiv = html"""<div typoProperty="xx">content</div>"""
                            ^
typo.scala:24: value typoMethod is not a member of org.scalajs.dom.HTMLDivElement
        myDiv.value.typoMethod()
                    ^

With the help of the static type system, @html methods can be much more robust than ReactJS components.

You can find a complete list of supported properties and methods on scaladoc of scalajs-dom or MDN

Showcases

(Feel free to add your project here)

Modules

Binding.scala has an extremely tiny code base. The source files are split into few libraries, one file per library.

Core data-binding expressions (Binding.scala)

This module is available for both JVM and Scala.js. You could add it in your build.sbt.

// For JVM projects
libraryDependencies += "com.thoughtworks.binding" %% "binding" % "latest.release"
// For Scala.js projects, or JS/JVM cross projects
libraryDependencies += "com.thoughtworks.binding" %%% "binding" % "latest.release"

HTML DOM integration (html.scala)

This is the new HTML templating library based on Name Based XML Literals, the module is only available for Scala.js, and the Scala version must between 2.12 and 2.13. You could add it in your build.sbt.

// Enable macro annotations by setting scalac flags for Scala 2.13
scalacOptions ++= {
  import Ordering.Implicits._
  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))
  }
}

// For Scala.js projects (Scala 2.12 - 2.13)
libraryDependencies += "com.yang-bo" %%% "html" % "latest.release"

See html.scala for more information.

Remote data-binding for scala.concurrent.Future (FutureBinding.scala)

This module is available for both JVM and Scala.js. You could add it in your build.sbt.

// For JVM projects
libraryDependencies += "com.thoughtworks.binding" %% "futurebinding" % "latest.release"
// For Scala.js projects, or JS/JVM cross projects
libraryDependencies += "com.thoughtworks.binding" %%% "futurebinding" % "latest.release"

See FutureBinding for more information.

Remote data-binding for ECMAScript 2015's Promise (JsPromiseBinding.scala)

This module is only available for Scala.js. You could add it in your build.sbt.

// For Scala.js projects
libraryDependencies += "com.thoughtworks.binding" %%% "jspromisebinding" % "latest.release"

See FutureBinding for more information.

Requirements

Due to collection API changes, Binding.scala 12.x only works on Scala 2.13, targeting JVM, Scala.js 0.6 and Scala.js 1.x.

For Scala 2.10, 2.11 and 2.12 on JVM or Scala.js 0.6, use Binding.scala 11.x instead.

Related projects

Other links

binding.scala's People

Contributors

adrobisch avatar ajaychandran avatar atry avatar bbarker avatar carueda avatar djx314 avatar github-actions[bot] avatar gitter-badger avatar glmars avatar jason-cooke avatar kahliburke avatar kitsudog avatar maowug avatar marisakirisame avatar olivierblanvillain avatar psycho7 avatar scala-steward avatar williamho avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

binding.scala's Issues

`insert` has unexpected side effects

I'm trying to insert items at specific positions in a Vars list. I notice that calling insert on the buffer created by Vars#get has some unexpected side effects. Specifically, after calling insert and then calling a method that removes elements (such as .clear() or .trimEnd(...)), it doesn't remove elements that I expect.

I've created a ScalaFiddle to demonstrate the issue:
https://scalafiddle.io/sf/SBPcr3y/0

Click "Add random item" a few times and then click "Clear". Not all items get removed from the UI, though it says there are 0 items in the list. Is this a bug or am I using it incorrectly?

import com.thoughtworks.binding
import com.thoughtworks.binding.Binding._
import org.scalajs.dom.raw._
import org.scalajs.dom.document
import scala.scalajs.js.annotation.JSExport
import scala.scalajs.js.JSApp
import scala.util.Random

val initialItems = (1 to 10).map(i => s"initial item $i")
val items = Vars(initialItems: _*)

/**
 * Add a few items and click "Clear". Not all items get cleared,
 * and the length is incorrect.
 */
binding.dom.render(document.body, listElem(items))

def insertRandom(items: Vars[String]) = {
  val buffer = items.get
  val index = if (buffer.isEmpty) 0 else Random.nextInt(buffer.length)
  buffer.insert(index, s"new item ${Random.nextInt.abs}")
}

@binding.dom
def listElem(listItems: BindingSeq[String]) = {
  <div>total items: { listItems.bind.length.toString }</div>
  <button onclick={ (e: Event) => insertRandom(items) }>Add random item</button>
  <button onclick={ (e: Event) => items.get.clear() }>Clear</button>
  <ol>{
    listItems.map { item =>
      <li>{ item }</li>
    }
  }</ol>
}

Customize tags with inner html

For example, https://ant.design/components/popconfirm/

// confirm, cancel are functions
<Popconfirm title="Are you sure delete this task?" onConfirm={confirm} onCancel={cancel} okText="Yes" cancelText="No">
    <a href="#">Delete</a>
</Popconfirm>

What I expect:

class Popconfirm(title: String, okText: String, cancelText: String, onConfirm: Event => Unit, onCancel: Event => Unit) extend SomeTrait {
  @some_anno override def render(body: Element): Element = ???
}

here render is the function used in macros.

But this test case(https://github.com/ThoughtWorksInc/Binding.scala/blob/11.0.x/fxml/.js/src/test/scala/com/thoughtworks/binding/DateSpec.scala) shows that <Date></Date> is Binding[Date] or another type relating to Date. It seems @fxml is not suitable for customizing tags to create something like Ant Design.

In #4 (comment) ,
there are tricks to make custom tags work, but it is without any inner html (eg. <a href="#">Delete</a>).

Nested elements

It seems like accessing nested elements after constructing them using XML literals doesn't work:

val div = <div><div>Nested</div></div>
div.childNodes.length == 0

Rendering div afterwards works like expected though.

Example: https://scalafiddle.io/sf/6ZzWXB2/2
This should print

nested div: [object HTMLDivElement]
div children: 1

Auto inline val definition

Look at following example,

val str = Var[String]("foo")
@dom def foo() = {
   val strBind = str.bind
   <div>
     <span>{strBind}</span>
   </div>
}

The val strBind = str.bind makes the whole block rerendered while str changed.
Auto inline it could make dom change minimal

Empty string in value of html tag option

import com.thoughtworks.binding._
import org.scalajs.dom._


@dom def main = {
  <select>
  <option value="">Volvo</option>
  <option value="saab">Saab</option>
  <option value="opel">Opel</option>
  <option value="audi">Audi</option>
  </select>
}

dom.render(document.body, main)
ScalaFiddle.scala:8: error: type mismatch;
 found   : scala.collection.immutable.Nil.type
 required: String
    Volvo
     ^

see https://scalafiddle.io/sf/SYResoO/1

P.S. in scalafiddle I use Binding.scala 9.0.3, since newer versions do not work in scalafiddle

scalafmt fails when using html

object Something {

  @dom
  def apply(user: Var[Option[User]], logoutCall: () => Unit): Binding[Div] = {
<div>
{
   user.bind match {
      case None =>
         <div/>
      case Some(_) =>
         <a class = "btn btn-secondary" style = "float:right;" onclick = { (e: Event) ⇒ logoutCall() }>
            Logout
         </a>
     }
   }
</div>
}

in terminal
>scalafmt
[trace] Stack trace suppressed: run last web-client/compile:scalafmt for the full output.
error java.lang.IncompatibleClassChangeError: Found class fastparse.core.Parser, but interface was expected

>last web-client/compile:scalafmt
java.lang.IncompatibleClassChangeError: Found class fastparse.core.Parser, but interface was expected
at scala.meta.internal.tokenizers.LegacyScanner.getXml(LegacyScanner.scala:959)
at scala.meta.internal.tokenizers.LegacyScanner.fetchLT$1(LegacyScanner.scala:336)
at scala.meta.internal.tokenizers.LegacyScanner.fetchToken(LegacyScanner.scala:344)
at scala.meta.internal.tokenizers.LegacyScanner.nextToken(LegacyScanner.scala:233)
at scala.meta.internal.tokenizers.LegacyScanner.foreach(LegacyScanner.scala:1022)
at scala.meta.internal.tokenizers.ScalametaTokenizer.uncachedTokenize(ScalametaTokenizer.scala:154)
at scala.meta.internal.tokenizers.ScalametaTokenizer.tokenize(ScalametaTokenizer.scala:34)
at scala.meta.internal.tokenizers.ScalametaTokenizer$$anon$2.apply(ScalametaTokenizer.scala:263)
at scala.meta.tokenizers.Api$XtensionTokenizeDialectInput.tokenize(Api.scala:23)
at scala.meta.tokenizers.Api$XtensionTokenizeInputLike.tokenize(Api.scala:12)
at scala.meta.internal.parsers.ScalametaParser.scannerTokens$lzycompute(ScalametaParser.scala:207)
at scala.meta.internal.parsers.ScalametaParser.scannerTokens(ScalametaParser.scala:207)
at scala.meta.internal.parsers.ScalametaParser.(ScalametaParser.scala:168)
at scala.meta.internal.parsers.ScalametaParser$$anon$201.apply(ScalametaParser.scala:3650)
at scala.meta.parsers.Api$XtensionParseDialectInput.parse(Api.scala:21)
at scala.meta.parsers.Api$XtensionParseInputLike.parse(Api.scala:10)
at org.scalafmt.Scalafmt$.format(Scalafmt.scala:51)
at org.scalafmt.cli.Scalafmt210.format(Scalafmt210.scala:46)
at org.scalafmt.cli.Scalafmt210.format(Scalafmt210.scala:30)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.scalafmt.sbt.HasScalaFmt.org$scalafmt$sbt$HasScalaFmt$$handleFile(HasScalaFmt.scala:85)
at org.scalafmt.sbt.HasScalaFmt$$anonfun$writeFormattedContentsToFiles$1$$anonfun$apply$3.apply(HasScalaFmt.scala:54)
at org.scalafmt.sbt.HasScalaFmt$$anonfun$writeFormattedContentsToFiles$1$$anonfun$apply$3.apply(HasScalaFmt.scala:54)
at scala.collection.Iterator$class.foreach(Iterator.scala:727)
at scala.collection.parallel.immutable.ParHashSet$ParHashSetIterator.foreach(ParHashSet.scala:76)
at scala.collection.parallel.ParIterableLike$Foreach.leaf(ParIterableLike.scala:975)
at scala.collection.parallel.Task$$anonfun$tryLeaf$1.apply$mcV$sp(Tasks.scala:54)
at scala.collection.parallel.Task$$anonfun$tryLeaf$1.apply(Tasks.scala:53)
at scala.collection.parallel.Task$$anonfun$tryLeaf$1.apply(Tasks.scala:53)
at scala.collection.parallel.Task$class.tryLeaf(Tasks.scala:56)
at scala.collection.parallel.ParIterableLike$Foreach.tryLeaf(ParIterableLike.scala:972)
at scala.collection.parallel.AdaptiveWorkStealingTasks$WrappedTask$class.compute(Tasks.scala:165)
at scala.collection.parallel.AdaptiveWorkStealingForkJoinTasks$WrappedTask.compute(Tasks.scala:514)
at scala.concurrent.forkjoin.RecursiveAction.exec(RecursiveAction.java:160)
at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)
at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)
error java.lang.IncompatibleClassChangeError: Found class fastparse.core.Parser, but interface was expected

Object FutureBinding is not a member of package com.thoughtworks.binding @v9.0.1

I'm trying to use FutureBinding with this build config:

libraryDependencies += "com.thoughtworks.binding" %%% "futurebinding" % "latest.release"
libraryDependencies += "com.thoughtworks.binding" %%% "dom" % "latest.release"
enablePlugins(ScalaJSPlugin)
addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.0" cross CrossVersion.full)

At this point latest.release resolved to the version 9.0.1.

And compiler report the following when compiling:

[error] ....../component/ConnectDialog.scala:4: object FutureBinding is not a member of package com.thoughtworks.binding
[error] import com.thoughtworks.binding.FutureBinding
[error]        ^
[error] ....../component/ConnectDialog.scala:36: not found: value FutureBinding
[error]       {val result = FutureBinding(Ajax.get(s"mock/2"))
[error]                           ^
[error] two errors found
[error] (compile:compileIncremental) Compilation failed
[error] Total time: 2 s, completed 2016-9-20 15:36:30

I have checked the Binding.scala-website project finding out it was compiling with version 8.0.0.

Then I was tried to switch version to 8.0.0 & 9.0.0, both of these versions works well.

Is there something changed at version 9.0.1?

Element not created when I fetch it.

I want to edit an element after it created dynamically(for example,draw a picture in a canvas).So I tried something like this.

import com.thoughtworks.binding.Binding.{ Var, Vars }
import com.thoughtworks.binding.dom
import org.scalajs.dom.raw.Event
import org.scalajs.dom.document
import org.scalajs.dom.html.TextArea


@dom
def render = {
    val div =
        <textarea id="myArea" value="value1"></textarea>
    val ele = document.getElementById("myArea")
    ele.asInstanceOf[TextArea].value = "value2"
    div
}

dom.render(document.body, render)

but ele is null so I can't edit it.Is there any workaround or any promise about when the element will be created so I can trigger the script.

invalid-id is not a valid identifier

This bug occures in 10.0.0-M3

@dom def invalidId = {
  <div id="invalid-id"></div>
}
play.sbt.PlayExceptions$UnexpectedException: Unexpected exception[IllegalArgumentException: requirement failed: invalid-id is not a valid identifier]
    at play.sbt.run.PlayReload$$anonfun$taskFailureHandler$1.apply(PlayReload.scala:51) ~[na:na]
    at play.sbt.run.PlayReload$$anonfun$taskFailureHandler$1.apply(PlayReload.scala:44) ~[na:na]
    at scala.Option.map(Option.scala:145) ~[scala-library.jar:na]
    at play.sbt.run.PlayReload$.taskFailureHandler(PlayReload.scala:44) ~[na:na]
    at play.sbt.run.PlayReload$.compileFailure(PlayReload.scala:40) ~[na:na]
    at play.sbt.run.PlayReload$$anonfun$compile$1.apply(PlayReload.scala:17) ~[na:na]
    at play.sbt.run.PlayReload$$anonfun$compile$1.apply(PlayReload.scala:17) ~[na:na]
    at scala.util.Either$LeftProjection.map(Either.scala:377) ~[scala-library.jar:na]
    at play.sbt.run.PlayReload$.compile(PlayReload.scala:17) ~[na:na]
    at play.sbt.run.PlayRun$$anonfun$playRunTask$1$$anonfun$apply$2$$anonfun$apply$3$$anonfun$2.apply(PlayRun.scala:61) ~[na:na]
Caused by: java.lang.IllegalArgumentException: requirement failed: invalid-id is not a valid identifier
    at scala.Predef$.require(Predef.scala:233) ~[scala-library.jar:na]
    at org.scalajs.core.ir.Trees$.requireValidIdent(Trees.scala:61) ~[na:na]
    at org.scalajs.core.ir.Trees$Ident.<init>(Trees.scala:45) ~[na:na]
    at org.scalajs.core.compiler.JSEncoding$class.encodeLocalSym(JSEncoding.scala:188) ~[na:na]
    at org.scalajs.core.compiler.GenJSCode.encodeLocalSym(GenJSCode.scala:31) ~[na:na]
    at org.scalajs.core.compiler.GenJSCode$JSCodePhase.genStatOrExpr(GenJSCode.scala:1589) ~[na:na]
    at org.scalajs.core.compiler.GenJSCode$JSCodePhase.genStat(GenJSCode.scala:1529) ~[na:na]
    at org.scalajs.core.compiler.GenJSCode$JSCodePhase$$anonfun$70.apply(GenJSCode.scala:2714) ~[na:na]
    at org.scalajs.core.compiler.GenJSCode$JSCodePhase$$anonfun$70.apply(GenJSCode.scala:2714) ~[na:na]
    at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244) ~[scala-library.jar:na]

Custom tags (ie: SVG)

Hello, is it possible to extend the list of supported tags and attributes ?

value svg is not a member of object com.thoughtworks.binding.dom.Runtime.TagsAndTags2

[error]   @dom def render = <svg></svg>

[![CDATA xml tags error. "overloaded method value domBindingSeq with alternatives:"

scalafiddle ok.

https://scalafiddle.io/sf/Sc0RmDj/3

but. desktop spit out errors.

code

  @dom def inlineStyle = {
    <section>
      <style>
        <![CDATA[
         .highlight {background-color:gold}
         ]]>
      </style>
      <p> Binding.scala. easy to use.
      </p>
    </section>
  }

sbt environment.

scala 2.12
scalajs 0.6.15 
libraryDependencies += "com.thoughtworks.binding" %%% "binding" % "latest.release"
libraryDependencies += "com.thoughtworks.binding" %%% "dom" % "latest.release"
libraryDependencies += "com.thoughtworks.binding" %%% "futurebinding" % "latest.release"
libraryDependencies += "com.thoughtworks.binding" %%% "jspromisebinding" % "latest.release"

error message

[error] /Users/baram204/IdeaProjects/learningPixi/src/main/scala/binding/L07_XHTMLliterals.scala:39: overloaded method value domBindingSeq with alternatives:
[error]   (text: String)com.thoughtworks.binding.Binding.Constants[org.scalajs.dom.raw.Text] <and>
[error]   (node: org.scalajs.dom.raw.Node)com.thoughtworks.binding.Binding.Constants[org.scalajs.dom.raw.Node] <and>
[error]   (seq: Seq[org.scalajs.dom.raw.Node])com.thoughtworks.binding.Binding.Constants[org.scalajs.dom.raw.Node] <and>
[error]   (bindingSeq: com.thoughtworks.binding.Binding.BindingSeq[org.scalajs.dom.raw.Node])com.thoughtworks.binding.Binding.BindingSeq[org.scalajs.dom.raw.Node]
[error]  cannot be applied to (scala.xml.PCData)
[error]         <![CDATA[
[error]                  ^

Whole `@dom` block is recreated while only part of them relies on `Binding` variables

https://scalafiddle.io/sf/uwj5QcD/2

import com.thoughtworks.binding.dom
import com.thoughtworks.binding.Binding.Vars
import org.scalajs.dom._

@dom
def render = {
  val logs = Vars("Hello", "Binding.scala")
  <div id="foo">
  
  <div>
    { for { log <- logs } yield <div>{ log }</div> }
  </div>
  <a href="#" onclick={(evt:Event) => logs.get += "foo";showProp()}>ClikeMe</a>
  </div>
}

def showProp() = {
  val e = document.getElementById("foo")
  if(e.hasAttribute("foo")) {
    window.alert(e.getAttribute("foo"))
  } else {
    e.setAttribute("foo", "bar")
  }
}

dom.render(document.body, render)

As above example indicates, the outer div[id=foo] seems recreated every time clicking the link.
This is absoultely not what precise-data-binding means

Incompatible with newest version 0.6.0 of ScalaTags

In build tools another dependency can easily pull in the newer version and the resulting macro errors are hard to diagnose for new users. It would make sense to update from 0.5.5 to 0.6.0 in order to make the library more approachable.

Add benchmarks page and test suite

Just a suggestion in case someone wants to have a crack at it (maybe I will, but I doubt ...):

This might be a good starting point, though note the comment at the bottom that explains the problem with raw dom manipulation.

Allow usage of custom tags defined in native js libraries

I read through the issues of custom tags but must admit, that i did not fully understand if it is possible to extend Binding.scala with custom tags.

Say OnsenUI defines a custom tag <ons-page>, how can i use this in Binding.scala?

I tried the following wrapper

package onsen

import com.thoughtworks.binding._
import Binding._
import org.scalajs.dom.document
import org.scalajs.dom.raw.Node

import scala.language.implicitConversions
import scala.scalajs.js
import scala.scalajs.js.annotation.JSName

case class OnsenBasicComponentBuilder(tagName: String) {

  def render = this

  var modifier: Binding[String] = _

  @dom def build = {
    val element=document.createElement(tagName)
    element.setAttribute("modifier",modifier.bind)
    element
  }
}

object Onsen {
  implicit def autoBinding[A](a: A): Binding[A] = ???

  implicit final class CustomTags(x: dom.Runtime.TagsAndTags2.type) {

    def OnsPage() = OnsenBasicComponentBuilder("ons-page")
    def OnsToolbar() = OnsenBasicComponentBuilder("ons-toolbar")
    def OnsToolbarButton() = OnsenBasicComponentBuilder("ons-toolbar-button")
    def OnsIcon() = OnsenBasicComponentBuilder("ons-icon")
    def OnsButton() = OnsenBasicComponentBuilder("ons-button")

  }

  implicit def toHtml(x: OnsenBasicComponentBuilder): BindingSeq[Node] = {
    Constants(x.build).mapBinding(identity)
  }
}

and use it as follows

import onsen.Onsen._
  @dom
  def page = {
    <div>
      <OnsPage><OnsToolbar></OnsToolbar></OnsPage>
    </div>
  }

getting

overloaded method constructor NodeSeqMountPoint with alternatives:
[error]   (parent: org.scalajs.dom.raw.Node,childBinding: com.thoughtworks.binding.Binding[org.scalajs.dom.raw.Node])com.thoughtworks.binding.dom.Runtime.NodeSeqMountPoint <and>
[error]   (parent: org.scalajs.dom.raw.Node,childBinding: com.thoughtworks.binding.Binding[com.thoughtworks.binding.Binding.BindingSeq[org.scalajs.dom.raw.Node]],dummy: Unit)com.thoughtworks.binding.dom.Runtime.NodeSeqMountPoint <and>
[error]   (parent: org.scalajs.dom.raw.Node,childrenBinding: com.thoughtworks.binding.Binding.BindingSeq[org.scalajs.dom.raw.Node])com.thoughtworks.binding.dom.Runtime.NodeSeqMountPoint
[error]  cannot be applied to (onsen.OnsenBasicComponentBuilder, com.thoughtworks.binding.Binding.BindingSeq[org.scalajs.dom.raw.Node])
[error]       <OnsPage><OnsToolbar></OnsToolbar></OnsPage>
[error]        ^

How can i use nativescript with Binding.scala?

I want to make mobile app quick and easy.
so, nativescript is looks good.

nativescript

There are similar project sri. but I don't want to use facade and learn complex ReactNative and convent new convert way.

so how can I use nativescript with Binding.scala?

I'm a newbee. So I just asking you some advice.

ScalaJS 1.x support

Hi, I would be very glad to have this at the moment. Are there any plans to support ScalaJS 1.0.0-M... ?

Converting from BindingSeq to ObservableList

I was investigating if Binding.scala's FXML support will work for one of my personal projects, and quickly ran into issue trying to create a simple table:

import javafx.application.Application
import javafx.stage._
import javafx.scene._
import javafx.scene.control._

import com.thoughtworks.binding.Binding.Constants
import com.thoughtworks.binding._

final class FxmlTest extends Application {
  @fxml override def start(primaryStage: Stage): Unit = {
    type StringTable = TableView[String]
    val scene: Binding[Scene] =
      <Scene>
        <StringTable> {Constants("Hello", "Flatmap")}</StringTable>
      </Scene>
    fxml.show(primaryStage, scene)
  }
}

object FxmlTest {
  def main(args: Array[String]): Unit = {
    Application.launch(classOf[FxmlTest], args: _*)
  }
}

Results in this error:

[error] [E1] src/mco/ui/FxmlTest.scala
[error]      type mismatch;
[error]       found   : com.thoughtworks.binding.Binding.Constants[String]
[error]       required: javafx.collections.ObservableList[String]
[error]      L16:        <StringTable> {Constants("Hello", "Flatmap")}</StringTable>
[error]      L16:         ^

It does not work if I use Seq instead, too.

I found a related issue on SO but could not find a way to trigger implicit conversion, if there is one.

unable to use @fxml with a java bean when defined in the same project

I tried this :

class CustomTag(){
  @BeanProperty var field : String = ""
  override def toString = s"Person: $field"
}

@JSExport
object Foo {
  @fxml def test() = { <CustomTag field="test"/> }
}

[error] java.lang.ClassNotFoundException: sth.pkg.CustomTag
[error] at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
[error] at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
[error] at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
[error] at java.lang.Class.forName0(Native Method)
[error] at java.lang.Class.forName(Class.java:264)
[error] at com.thoughtworks.binding.fxml$Macros.resolvePropertiesForJavaBean(fxml.scala:928)
[error] @FXML def test() = { }

I tried

class CustomTag(@BeanProperty var field:String){
  override def toString = s"Person: $field"
}

sth.pkg.CustomTag is not a Java Bean nor a type built from JavaFXBuilderFactory
[error] @FXML def test() = { }
[error] ^

I also tried to create a regular java class :

public class CustomTag {
    private String field;
    public CustomTag(){

    }

    public CustomTag(String field){
        this.field=field;
    }

    public String getField() {
        return field;
    }

    public void setField(String field) {
        this.field = field;
    }
}

and get java.lang.ClassNotFoundException:

I also tried to add an import next to it :

  @fxml def test() = {
      import sth.pkg.CustomTag


Can this library be used with IntelliJ?

I am using IntelliJ as my IDE and it underlines in red all my @dom functions. I tried to open the sample project at https://github.com/ThoughtWorksInc/todo/ and I have the same problem. Is there a way to get rid of this? I have looked at the docs and could not find it.
As it seems related to the use of the macro, is there an "easy" way to define a binding without the macro?

@fxml hangs after javafx has run in-JVM

To reproduce:

  1. Go to https://gist.github.com/nafg/b51c7a9c1c8022ba3ac5f2a45eb1d62d and click Download ZIP, extract into a new directory.
  2. Go into the directory and type sbt run clean compile
  3. When the little window appears, close it.
  4. The next compilation (forced because of the clean) hangs with this stack trace:
[error] /tmp/fxml/FxmlSample.scala:10: exception during macro expansion: 
[error] java.lang.InterruptedException
[error] 	at java.lang.Object.wait(Native Method)
[error] 	at java.lang.Object.wait(Object.java:502)
[error] 	at com.thoughtworks.binding.fxml$Macros$.<init>(fxml.scala:555)
[error] 	at com.thoughtworks.binding.fxml$Macros$.<clinit>(fxml.scala)
[error] 	at com.thoughtworks.binding.fxml$Macros$XmlTransformer$$anonfun$com$thoughtworks$binding$fxml$Macros$XmlTransformer$$transformXmlValue$1.applyOrElse(fxml.scala:1228)
[error] 	at com.thoughtworks.binding.fxml$Macros$XmlTransformer$$anonfun$com$thoughtworks$binding$fxml$Macros$XmlTransformer$$transformXmlValue$1.applyOrElse(fxml.scala:1219)
[error] 	at scala.PartialFunction$Lifted.apply(PartialFunction.scala:223)
[error] 	at scala.PartialFunction$Lifted.apply(PartialFunction.scala:219)
[error] 	at com.thoughtworks.Extractor$PartialFunctionToExtractor$$anon$3.unapply(Extractor.scala:98)
[error] 	at com.thoughtworks.binding.fxml$Macros$XmlTransformer$$anonfun$com$thoughtworks$binding$fxml$Macros$XmlTransformer$$expand$1.applyOrElse(fxml.scala:1415)
[error] 	at com.thoughtworks.binding.fxml$Macros$XmlTransformer$$anonfun$com$thoughtworks$binding$fxml$Macros$XmlTransformer$$expand$1.applyOrElse(fxml.scala:1414)
[error] 	at scala.PartialFunction$Lifted.apply(PartialFunction.scala:223)
[error] 	at scala.PartialFunction$Lifted.apply(PartialFunction.scala:219)
[error] 	at com.thoughtworks.Extractor$PartialFunctionToExtractor$$anon$3.unapply(Extractor.scala:98)
[error] 	at com.thoughtworks.binding.fxml$Macros$XmlTransformer$$anonfun$com$thoughtworks$binding$fxml$Macros$XmlTransformer$$transformBlock$1$$anonfun$37.apply(fxml.scala:1427)
[error] 	at com.thoughtworks.binding.fxml$Macros$XmlTransformer$$anonfun$com$thoughtworks$binding$fxml$Macros$XmlTransformer$$transformBlock$1$$anonfun$37.apply(fxml.scala:1425)
[error] 	at scala.collection.immutable.List.flatMap(List.scala:327)
[error] 	at com.thoughtworks.binding.fxml$Macros$XmlTransformer$$anonfun$com$thoughtworks$binding$fxml$Macros$XmlTransformer$$transformBlock$1.applyOrElse(fxml.scala:1425)
[error] 	at com.thoughtworks.binding.fxml$Macros$XmlTransformer$$anonfun$com$thoughtworks$binding$fxml$Macros$XmlTransformer$$transformBlock$1.applyOrElse(fxml.scala:1423)
[error] 	at scala.PartialFunction$Lifted.apply(PartialFunction.scala:223)
[error] 	at scala.PartialFunction$Lifted.apply(PartialFunction.scala:219)
[error] 	at com.thoughtworks.Extractor$PartialFunctionToExtractor$$anon$3.unapply(Extractor.scala:98)
[error] 	at com.thoughtworks.binding.fxml$Macros$XmlTransformer.transform(fxml.scala:1445)
[error] 	at com.thoughtworks.binding.fxml$Macros$$anonfun$macroTransform$1.apply(fxml.scala:1484)
[error] 	at com.thoughtworks.binding.fxml$Macros$$anonfun$macroTransform$1.apply(fxml.scala:1465)
[error] 	at com.thoughtworks.sde.core.Preprocessor.replaceDefBody(Preprocessor.scala:35)
[error] 	at com.thoughtworks.binding.fxml$Macros.macroTransform(fxml.scala:1464)

Invalid constructor arguments when using boolean attributes

[error] /Developer/test-sdk/shared/src/main/scala/com/naden/namaste/components/elements/CheckboxElement.scala:13:4: overloaded method constructor UnprefixedAttribute with alternatives:
[error]   (key: String,value: Option[Seq[scala.xml.Node]],next: scala.xml.MetaData)scala.xml.UnprefixedAttribute <and>
[error]   (key: String,value: String,next: scala.xml.MetaData)scala.xml.UnprefixedAttribute <and>
[error]   (key: String,value: Seq[scala.xml.Node],next1: scala.xml.MetaData)scala.xml.UnprefixedAttribute
[error]  cannot be applied to (String, Boolean, scala.xml.MetaData)
[error] 		<input name={ name.bind } type="checkbox" class="styled" checked={ checked.bind } />
[error] 		 ^

Appears for the following code:

case class CheckboxElement(name: Var[String],
            label: Var[String],
            checked: Var[Boolean] = Var(false)) extends Component {

	@dom def render() = {
		<label class="checkbox-inline checkbox-right">
		<input name={ name.bind } type="checkbox" class="styled" checked={ checked.bind } />
		{ label.bind }
		</label>
	}
}

If I remove the checked attribute the error message goes away.

Question (for FAQ?) - ease of integration with e.g. jQuery or React

One of the biggest pain points with any new framework is, I need a widget that does X, someone already implemented X, can I use their widget?

For example, with React using jQuery plugins is tricky (see for example http://tech.oyster.com/using-react-and-jquery-together/)

Of course there are already the cross-language issues, e.g. with scala.js you have to write a facade, and until http://scala-lang.org/blog/2016/10/19/scalajs-bundler.html came along there were the issues with using npm modules.

If I could somehow use Binding.scala for the page but throw React components and jQuery plugins inside in places, and/or use Binding.scala inside a React component, it would be a much easier sell (to myself).

I'm guessing that would require lifecycle hooks (similar to using jQuery with React).

It would be nice to have instructions how to achieve this (or at least a FAQ saying it's not possible, if that's the case).

Thanks!

port @dom to Scalameta

Per Yang's suggestion on gitter:

@dom itself could be implemented in scala.meta, as @dom is barely a syntax sugar, forwarding .bind magic to macros in Each/SDE.

There are two steps to compile a @dom method

  1. @dom translate your XHTML literal and bind magic to some SDE instructrions
  2. The SDE macro perform monadic translation.

SDE (Specific Domain Extension) is the name of core library shared by both Binding.scala and Each.

... investigate if it is possible to implement a scala.meta version of @dom. The current implemention is merely 431 lines of code. https://github.com/ThoughtWorksInc/Binding.scala/blob/10.0.x/dom/src/main/scala/com/thoughtworks/binding/dom.scala

The Binding trait is not extendable.

Currently the Binding trait can not be extended easily because some private method and type.
We need this because we want to make our existing code(which has it's own data-binding mechanism) work with Binding.scala together. We want to implement Binding trait for our own data type but find no way to do this.
Do you have a plan to change this ? Or am I missing something?

Creating a BindingSeq

I am trying to create a function layout which will return multiple nodes.

 @dom
  def renderLayout(): Binding[BindingSeq[Node]] = {
    val bSNavBar = new BSNavBar(MockData.menuData)
    <div class="container">
      {bSNavBar.renderNavBar.bind}
    </div>

      <div class="container">
        {bSNavBar.renderNavBar.bind}
      </div>

  }

If fails to compile with type mismatch : found NodeBuffer
Also the code mentioned in documentation does not compile. type mismatch : found NodeBuffer.
@dom def node: Binding[BindingSeq[org.scalajs.dom.raw.HTMLBRElement]] = <br/><br/>

How to return multiple nodes ?

Each instructions message appears randomly

The following code produces an "each instructions must be inside a SDE block" error message even though it doesn't doesn't match any of the conditions for when it should normally appear e.g. not within @dom section.

case class CheckboxElement(name: Var[String],
            label: Var[String],
            checked: Var[Boolean] = Var(false)) extends Component {

	@dom def render() = {
		<label class="checkbox-inline checkbox-right">
		<input name={ name.bind } type="checkbox" class="styled"/>
		{ label.bind }
		</label>
	}
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.