-
Notifications
You must be signed in to change notification settings - Fork 102
Binding.scala快速上手指南
Binding.scala 的基本用法很像 ReactJS ,也是通过编写模板把数据映射成 HTML 。
首先,你需要设置项目的构建脚本。Binding.scala 就是普通的 Scala.js 库,按普通的 Scala.js 项目配置方式即可。具体参考 Scala.js 的教程或者TodoMVC的源码仓库。
修改你的 build.sbt 文件,加入以下两行
libraryDependencies += "com.thoughtworks.binding" %%% "dom" % "latest.release"
addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.0" cross CrossVersion.full)@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)、render和i都是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 时,spinner 和 render 中的 i.bind 都会触发,实现了类似双向绑定的效果。
编写完功能后,你需要在 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。