-
Notifications
You must be signed in to change notification settings - Fork 108
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
。