Binding.scala快速上手指南

杨博 (Yang Bo) edited this page Feb 4, 2017 · 12 revisions

基本用法

Binding.scala 的基本用法很像 ReactJS ,也是通过编写模板把数据映射成 HTML 。

第零步:构建脚本配置

首先,你需要设置项目的构建脚本。Binding.scala 就是普通的 Scala.js 库,按普通的 Scala.js 项目配置方式即可。具体参考 Scala.js 的教程或者TodoMVC的源码仓库

第一步:添加 Binding.scala 的依赖

修改你的 build.sbt 文件,加入以下两行

libraryDependencies += "com.thoughtworks.binding" %%% "dom" % "latest.release"

addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.0" cross CrossVersion.full)

第二步:定义 HTML 模板

@dom
def spinner(i: Var[Int]) = {
  <div>
    <button onclick={ event: Event => i := i.get - 1 }>-</button>
    { i.bind.toString }
    <button onclick={ event: Event => i := i.get + 1 }>+</button>
  </div>
}

@dom
def render = {
  val i = Var(0)
  <div>
    { spinner(i).bind }
    控件当前值是:{ i.bind.toString }
  </div>
}

你可以在ScalaFiddle实际运行一下试试。

Scala 代码的语法和 ECMAScript 6 很接近。有 JavaScript 经验的同学只要参考 Scala.js for JavaScript developers 就可以迁移到 Scala.js 了。

以上 Scala 代码创建了两个 HTML 模板,其中 spinner 是个数字选择组件,render 是页面的内容。Binding.scala 支持 Scala 代码和 HTML 模板混合编写,所以 onclick={...} 把一个 Scala 函数设置给了 HTML 属性 onclick

上述代码用到了 Binding.scala 的几个API:

  • @dom 把方法标记成绑定表达式,在这个方法内可以编写 HTML 模板、可以使用 .bind 监听其他 HTML 模板或变量。
  • Binding 是绑定表达式。上面代码的 spinner(i)renderi 都是 Binding
    • Binding.bind 用来监听绑定表达式。

      xxx.bind的含义是“each time xxx changes”。比如 @dom def j = i.bind + 5 表示每当 i 改变时,j也改变,j的值是 i 的值加5。

      注意:bind表示绑定表达式之间的依赖关系,所以只能写在绑定表达式的内部。bind不是普通的函数!

  • Var 是可以被监听的变量,Var继承了Binding。上面代码中的 i 就是个 Var
    • Var.bind 继承自 Binding.bind,表示要监听变量的值。
    • Var.get 获取变量的当前值,不能写在绑定表达式中。
    • := 对变量的赋值,并重新计算所有依赖该变量的绑定表达式,不能写在绑定表达式中。

注意:{ event: Event => i := i.get + 1 }没加 @dom ,是个普通的函数而不是绑定表达式,所以其中可以使用 get:=

这两个 HTML 模板之间传递了绑定变量 i 作为参数。所以,当 spinner 内的 onclick 修改 i 时,spinnerrender 中的 i.bind 都会触发,实现了类似双向绑定的效果。

第三步:渲染 HTML

编写完功能后,你需要在 main 中把 render 渲染出来。

@JsExport def main() = {
  dom.render(document.body, render)
}
  • dom.render 接受两个参数,第一个参数表示要把模板渲染到哪个位置,第二个参数是将要渲染的模板。

最后在静态 HTML 中调用 main() 方法:

<script type="text/javascript"> SampleMain().main() </script>

这样一来整个程序就写完了。

容器和循环

复杂的网页上往往会有重复的元素,可以把数据保存到 Vars 容器,然后用 for / yield 渲染。

比如

@dom def render = {
  val logs = Vars("Hello", "Binding.scala")
  <div>
    { for { log <- logs } yield <div>{ log }</div> }
  </div>
}

以上代码通过 for / yield 语句渲染了两行文字,你可以在ScalaFiddle实际运行一下试试

  • Vars 是可以被监听的容器。所以,每当 Vars 的内容修改时,渲染出的网页也会自动修改:
@dom def render = {
  val logs = Vars("Input code:")
  val keyDownHandler = { event: KeyboardEvent =>
    (event.currentTarget, event.keyCode) match {
      case (input: html.Input, KeyCode.Enter) =>
        event.preventDefault()
        logs.get += input.value
        logs.get += js.eval(input.value).toString
        input.value = ""
      case _ =>
    }
  }
  <div>
    { for { log <- logs } yield <div>{ log }</div> }
    <input type="text" onkeydown={ keyDownHandler }/>
  </div>
}

以上代码实现了一个代码控制台,你可以在输入框中输入代码,按下回车键,就能看到代码执行结果。 你可以在ScalaFiddle实际运行一下试试。

Vars.get 类似 Var.get,用来获取容器的当前值,返回值是个 scala.collection.mutable.Buffer。 所以你还可以对 Var.get 取得的当前值调用 Buffer+= 等各种方法,直接修改容器的当前值。 当你修改这个 Buffer 时,会触发 Binding.scala 自动执行对应操作,把修改集映射到 for / yield 生成的页面上。

比如,logs.get += input.value 添加了一个字符串,那么渲染出的网页上也会添加一行包含该字符串的 <div> ,而不需要修改网页上原有的其他部分。

注意,Vars.get+= 就像 Var.get:= 一样,也不能在@dom绑定表达式或代码模板中调用,而只能在其他函数中调用。 比如以上代码的 submitHandler 回调函数不是一个 @dom 函数,所以可以在其中修改 logs

You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.