Is it save to assume, that Signal::map, which ends up in
a ComputedSignal, is the proper way to “zip” signals? Empirical
evidence seems fine.
E.g.
val tick = ValueSignal(Instant.now().toEpochMilli())
val zoneName: ValueSignal<String> = ValueSignal(ZoneId.systemDefault().toString())
val now: Signal<LocalTime> = tick.map { // XXX
Instant
.ofEpochMilli(it)
.atZone(
ZoneId.of(
zoneName.value() // XXX
)
)
.toLocalTime()
}
My full example
class ClockSegment(number: Signal<Int>) : Span() {
init {
ComponentEffect.bind(this, number, { c, v -> c.setText("%02d".format(v)) })
style.set("font-family", "monospace")
}
}
class DayOrNight(hour: Signal<Int>) : Span() {
val day = hour.map { it in 6..18 }
val night = day.map { !it }
init {
val dayDisplay = VaadinIcon.SUN_O.create()
val nightDisplay = VaadinIcon.MOON_O.create()
add(dayDisplay, nightDisplay)
ComponentEffect.bind(dayDisplay, day, Component::setVisible)
ComponentEffect.bind(nightDisplay, night, Component::setVisible)
}
}
class Clock(value: Signal<LocalTime>) : Div() {
val hour = value.map(LocalTime::getHour)
val minute = value.map(LocalTime::getMinute)
val second = value.map(LocalTime::getSecond)
init {
add(
DayOrNight(hour),
ClockSegment(hour), Span(":"),
ClockSegment(minute), Span(":"),
ClockSegment(second)
)
}
}
@Route(value = "", layout = MainLayout::class)
@PageTitle("Main")
class MainView() : Div() {
init {
val tick = ValueSignal(Instant.now().toEpochMilli())
val zoneName: ValueSignal<String> = ValueSignal(ZoneId.systemDefault().toString())
val zones = ComboBox<String>().apply {
setItems(ZoneId.getAvailableZoneIds().sorted())
setValue(zoneName.value())
// Fails because already in TX ComponentEffect.bind(this, zoneName, ComboBox<String>::setValue )
addValueChangeListener { zoneName.value(it.value) }
}
val now: Signal<LocalTime> = tick.map {
Instant
.ofEpochMilli(it)
.atZone(
ZoneId.of(
zoneName.value()
)
)
.toLocalTime()
}
val clock = Clock(now)
add(clock, zones)
Executors.newScheduledThreadPool(1).scheduleAtFixedRate({
clock.ui.map {
it.access {
tick.value(Instant.now().toEpochMilli())
}
}
}, 1, 1, TimeUnit.SECONDS)
}
}