Typed counter
A LiveViewtrait LiveView[Msg, Model]Defines a server-rendered view with typed messages and model state. with a count model and Decrement, Reset, and Increment
messages.
final class CounterExample extends LiveView[CounterExample.Msg, CounterExample.Model]:
import CounterExample.*
def mount(ctx: MountContext): Task[Model] =
ZIO.succeed(Model(count = 0))
def handleMessage(model: Model, ctx: MessageContext) =
case Msg.Decrement => ZIO.succeed(model.copy(count = model.count - 1))
case Msg.Increment => ZIO.succeed(model.copy(count = model.count + 1))
case Msg.Reset => ZIO.succeed(model.copy(count = 0))
override def view(model: Signal[Model]): HtmlElement[Msg] =
div(
cls := "docs-counter",
p(
cls := "docs-counter-status",
role := "status",
aria.live := "polite",
aria.atomic := true,
span(
cls := "docs-counter-model-label",
span("Current model"),
code("count =")
),
strong(cls := "docs-counter-value", model.map(_.count.toString))
),
fieldSet(
dataAttr("example-controls") := "",
legend(cls := "docs-visually-hidden", "Counter controls"),
button(
typ := "button",
cls := "docs-counter-secondary",
on.click(Msg.Decrement),
"Decrease"
),
button(
typ := "button",
cls := "docs-counter-secondary",
on.click(Msg.Reset),
"Reset"
),
button(
typ := "button",
cls := "docs-counter-increment",
on.click(Msg.Increment),
"Increase"
)
)
)
end CounterExample
object CounterExample:
enum Msg:
case Decrement, Increment, Reset
final case class Model(count: Int)View source (documentation/site/src/scalive/docs/examples/CounterExample.scala:8-63)
Result
ConnectedReconnectingRead-onlyCurrent modelcount =0
Disconnected. Controls resume after reconnection.
Related guidance: start with models, typed messages, and rendering.