Binding.scala: как вызвать побочные эффекты при обновлении значения?

У меня есть список географических координат в моем приложении, хранящийся в файле Vars[Coordinates]. Теперь я хотел бы отображать их на карте и автоматически обновлять карту, когда новые координаты добавляются в список или удаляются из него. Я использую библиотеку, которая предоставляет API для добавления и удаления маркеров на карте, поэтому я хотел бы вызывать их при обновлении списка, но я не могу найти никакого очевидного способа сделать это. Любые подсказки о том, как этого добиться?

edit: Спасибо @ Ян Бо за ответ! Я закончил с чем-то вроде этого:

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 для очистки.


person Matthias Berndt    schedule 26.02.2018    source источник
comment
Вызов побочного эффекта в HTML-шаблоне опасен. Я не думаю, что это хорошая идея, чтобы обеспечить элегантный синтаксический сахар для побочных эффектов.   -  person Yang Bo    schedule 19.03.2018
comment
Я должен сказать, что не согласен. Может это и опасно, но просто необходимо для того, чтобы переиспользовать библиотеки, которые взаимодействуют с DOM, так что я думаю это должно быть просто сделать. О, это твой проект ;-)   -  person Matthias Berndt    schedule 21.03.2018
comment
Это может быть редко используется, но я думаю, что это, тем не менее, важная функция. Хорошая структура должна упрощать повторное использование существующих библиотек, и я думаю, что в контексте привязки данных это означает, что я могу легко вызывать API-интерфейсы JavaScript, когда данные, которые я хочу отобразить, изменяются.   -  person Matthias Berndt    schedule 22.03.2018
comment
Если вы действительно заинтересованы в использовании атрибутов, на самом деле вы можете выполнять побочные эффекты для любых атрибутов, например: <canvas id="my_canvas" data:dummy_attribute={ crazyCanvasStuff(my_canvas, coordinates.all.bind); "dummy_value" }></canvas>   -  person Yang Bo    schedule 22.03.2018
comment
Обратите внимание, что MultiMountPoint может быть более эффективным, чем coordinates.all.bind, поскольку MultiMountPoint поддерживает частичное обновление.   -  person Yang Bo    schedule 22.03.2018
comment
Обратите внимание, что все описанные выше подходы к выполнению побочных эффектов не рекомендуются, поскольку их поведение зависит от внутренней реализации Binding.scala. Использование точек монтирования — единственный правильный способ реализации побочных эффектов, потому что только точки монтирования могут управлять жизненным циклом ваших пользовательских объектов, созданных во время рендеринга.   -  person Yang Bo    schedule 22.03.2018
comment
Да, я понял, что у других способов есть недостатки, поэтому я выбрал подход MultiMountPoint, и он, кажется, работает. Но что заставляет вас чувствовать себя странно, так это то, что вам, по-видимому, нужно вызывать .bind, а затем нигде не использовать его возвращаемое значение. Это не только выглядит странно, но и приводит к предупреждению компилятора, от которого я не знаю, как избавиться.   -  person Matthias Berndt    schedule 22.03.2018
comment
Давайте продолжим это обсуждение в чате.   -  person Matthias Berndt    schedule 22.03.2018


Ответы (2)


Вы можете создать свой собственный MultiMountPoint, который переопределяет методы splice и set.

Затем введите точку монтирования в процесс рендеринга, аналогично https://stackoverflow.com/a/45101054/955091 .


В отличие от Haskell, в Scala можно выполнять побочные эффекты везде, даже в HTML-шаблоне. Если вы написали что-то вроде <canvas id="my_canvas" data:dummy_attribute={ crazyCanvasStuff(my_canvas, coordinates.all.bind); "dummy_value" }></canvas>, это тоже должно работать.

Однако другие решения для реализации побочных эффектов не рекомендуются, поскольку их поведение зависит от внутренней реализации Binding.scala. Например, побочные эффекты могут не подвергаться повторной оценке, если среда выполнения Binding.scala находит восходящие данные без изменений (маршрутизатор Binding.scala ). Использование точек монтирования — единственный правильный способ реализовать побочные эффекты, особенно когда вам нужно частичное обновление или вы хотите управлять жизненным циклом ваших пользовательских объектов, созданных во время рендеринга.

person Yang Bo    schedule 26.02.2018

Я использую 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)
person Joe    schedule 06.03.2018