Binding.scala: 如何在更新值时调用副作用?

Binding.scala: How do I invoke side effects when a value is updated?

我的应用程序中有一个地理坐标列表,存储在 Vars[Coordinates] 中。我现在想在地图上显示这些,并在列表中添加或删除新坐标时自动更新地图。我正在使用一个公开 API 的库来在地图上添加和删除标记,因此我想在更新列表时调用这些标记,但我找不到任何明显的方法来做到这一点。关于如何实现这一目标的任何提示?

编辑:感谢@杨博的回复! 我最终得到了这样的结果:

val coordinates = Vars.empty[Coordinates]
def mapMountPoint(parent: Element, coordinates: BindingSeq[Coordinates]) =
  new MultiMountPoint[Coordinates](coordinates) {
    … // method overrides here to create the map in parent
  }
@dom 
def map = {
  val e = <div></div>
  mapMountPoint(e, coordinates).bind
  e
}

它似乎有效,当 div 被呈现或从 DOM 中移除时调用挂载和卸载方法……但这真的应该如何完成吗?看起来有点奇怪,我在调用 .bind: a pure expression does nothing in statement position; multiline expressions might require enclosing parentheses.

的地方也收到编译器警告

在 monadic-html 库中,有一种相当优雅的方法:

<canvas mhtml-onmount={ e => crazyCanvasStuff(e) }></canvas>

还有一个匹配的 mhtml-onunmount 属性用于清理。

您可能想要创建自己的 MultiMountPoint,它会覆盖 spliceset 方法。

然后将你的挂载点注入到渲染过程中,类似于


与 Haskell 不同,您可以在 Scala 中的任何地方执行副作用,甚至在 HTML 模板中。如果你写了类似 <canvas id="my_canvas" data:dummy_attribute={ crazyCanvasStuff(my_canvas, coordinates.all.bind); "dummy_value" }></canvas> 的东西,它应该也能工作。

但是,推荐执行副作用的其他解决方案,因为它们的行为取决于Binding.scala的内部实现。例如,当 Binding.scala 运行时查找上游数据未更改 () 时,可能不会重新评估副作用。使用挂载点是执行副作用的唯一正确方法,尤其是当您需要部分更新或者您想要管理在渲染期间创建的自定义对象的生命周期时。

我用 https://github.com/rtimush/scalatags-rx

build.sbt: libraryDependencies += "com.timushev" %%% "scalatags-rx" % "0.3.0"

示例:按下按钮时的机会颜色,使用 scalatags ...

var colorActiv = "green"
var colorInActiv = "black"

val buttonDateColor = Var(colorInActiv) // big V in Var
val buttonDateRx = Rx(buttonDateColor())

...

def buttonDateOnclick(): Unit = {
    if (buttonDateColor.now == colorInActiv){
        buttonDateColor() = colorActiv
    } else {
        buttonDateColor() = colorInActiv
    }
}

... // 在 Menubuttons 对象中:

var menuButtons =
div(
    button(
        menubuttonClass,
        color := buttonDateRx,
        onclick := buttonDateOnclick _,
        "button-text"
    )

... 在 main/initialize():

dom.document.getElementById("buttonsdiv").appendChild(Menubuttons.menuButtons.render)