1

I have a list of geographic coordinates in my application, stored in a Vars[Coordinates]. I'd now like to display these on a map and automatically update the map when new coordinates are added to or removed from the list. I'm using a library that exposes APIs to add and remove markers on a map, so I'd like to invoke these when the list is updated, but I can't find any obvious way to do that. Any hints on how to achieve this?

edit: Thanks @ Yang Bo for responding! I ended up with something like this:

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
}

It appears to work, the mount and unmount methods are called when the div is rendered or removed from the DOM… But is this really how it's supposed to be done? It looks a bit weird, and I also get a compiler warning where I call .bind: a pure expression does nothing in statement position; multiline expressions might require enclosing parentheses.

In the monadic-html library, there is a rather elegant way to do it:

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

There is also a matching mhtml-onunmount attribute for cleanup.

Yang Bo
  • 3,586
  • 3
  • 22
  • 35
Matthias Berndt
  • 4,387
  • 1
  • 11
  • 25
  • Invoking side effect is dangerous in an HTML template. I don't think it's a good idea to provide an "elegant" syntactic sugar for side effects. – Yang Bo Mar 19 '18 at 08:03
  • I have to say I disagree. Maybe it is dangerous, but it's simply necessary in order to reuse libraries that interact with the DOM, so I think it should be simple to do. Oh well, it's your project ;-) – Matthias Berndt Mar 21 '18 at 07:29
  • `MultiMountPoint` seems like a [rarely used feature](https://github.com/search?utf8=%E2%9C%93&q=com.thoughtworks.binding.Binding.MultiMountPoint&type=Code). Do you really think it is worth to assign a special syntax for that? – Yang Bo Mar 21 '18 at 09:42
  • It may be rarely used, but I think it's nevertheless an important feature. A good framework should make it easy to reuse existing libraries, and I think in a data-binding context this means I can easily invoke JavaScript APIs when the data I want to display changes. – Matthias Berndt Mar 21 '18 at 22:34
  • You don't need a special attribute to perform side effects, see solution 1 in https://stackoverflow.com/a/45101054/955091 – Yang Bo Mar 22 '18 at 03:47
  • If you are really keen to use attributes, actually you can perform side effects on **any** attributes like this: `` – Yang Bo Mar 22 '18 at 03:52
  • Note that `MultiMountPoint` can be more efficient than `coordinates.all.bind` because `MultiMountPoint` supports partial update. – Yang Bo Mar 22 '18 at 03:56
  • Notice that all the above approaches to perform side effects are **not** recommended, because the behavior depends on Binding.scala's internal implementation. Using mount points is the only right way to perform side effects, because only mount points can manage of the life cycle of your custom objects created during rendering. – Yang Bo Mar 22 '18 at 04:04
  • Yes, I understood that the other ways have downsides, that is why I did go with the MultiMountPoint approach and that seems to work. But what makes it feel weird is how you apparently need to call `.bind` without then using its return value anywhere. Not only does it look odd, it also leads to a compiler warning that I don't know how to get rid of. – Matthias Berndt Mar 22 '18 at 11:41
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/167336/discussion-between-matthias-berndt-and-yang-bo). – Matthias Berndt Mar 22 '18 at 11:41

2 Answers2

1

You may want to create your own MultiMountPoint, which overrides the splice and set methods.

Then inject your mount point into the rendering process, similar to https://stackoverflow.com/a/45101054/955091 .


Unlike Haskell, You can perform side effects in Scala everywhere, even in an HTML template. If you wrote something like <canvas id="my_canvas" data:dummy_attribute={ crazyCanvasStuff(my_canvas, coordinates.all.bind); "dummy_value" }></canvas>, it should work as well.

However, other solutions to perform side effects are not recommended, because their behaviors depend on Binding.scala's internal implementation. For example, the side effects may be not reevaluated when the Binding.scala runtime find upstream data is unchanged (Binding.scala router). Using mount points is the only right way to perform side effects, especially when you need partial updating or you want to manage of the life cycle of your custom objects created during rendering.

Yang Bo
  • 3,586
  • 3
  • 22
  • 35
-1

I use https://github.com/rtimush/scalatags-rx

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

example: chance color if a button is pressed, uses 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
    }
}

... // In Menubuttons object:

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

... In main/initialize():

dom.document.getElementById("buttonsdiv").appendChild(Menubuttons.menuButtons.render)
Joe
  • 85
  • 6