Open http://localhost:8080 and click +. The button POSTs to Inc, via renders Counter again, and the page morphs the new number in place.
Open a second tab
Click + in one tab. The other keeps the old number until its own next click or a reload: the count is shared, but nothing on this page pushes, so every click is one request and one response.
The count moves to a package store and a topic.Topic announces each change. Rendering a via.State makes the unit live: each tab opens one SSE stream and every publish reaches it.
load is StateTrack's reader: it runs at each init of the unit, so a new tab renders the current count. Dec is Inc with -1. via.Handler mounts Counter at / and returns the Router, which http.ListenAndServe serves on :8080.
The program above, running. Open this page in a second tab and click there: this card's Wire pane shows the patch arriving on this tab's stream with no POST from it. Clicks are capped at 30 a minute per client.
Try this
Open this page in a second tab, click + there, and come back.
Click + here: the POST answers 204 and the new number arrives on the stream.
Wire
Use the demo to see its requests and patches here.
Source
packagedemosimport("sync""github.com/go-via/via""github.com/go-via/via/h""github.com/go-via/via/on""github.com/go-via/via/topic")// startMu covers the change and its publish together, so publishes leave in// the order the count moved and the last one a tab sees is the current count.var(startMusync.MutexstartCountint64startMoved=topic.New[int64]())funcstartAdd(nint64){startMu.Lock()deferstartMu.Unlock()startCount+=nstartMoved.Publish(startCount)}funcstartLoad()int64{startMu.Lock()deferstartMu.Unlock()returnstartCount}funcresetStart(){startMu.Lock()deferstartMu.Unlock()startCount=0startMoved.Publish(0)}// StartCounter is the live counter from Getting started: every tab on the page// follows startCount through the topic, so a click in one redraws all of them.typeStartCounterstruct{// Limiter: see shared_contract.go.LimLimiterNvia.State[int64]}funcNewStartCounter(limLimiter)StartCounter{iflim==nil{panic("demos: NewStartCounter: StartCounter.Lim must not be nil")}returnStartCounter{Lim:lim,N:via.StateTrack(startMoved,startLoad)}}func(c*StartCounter)Inc(ctx*via.Ctx){ifc.Lim.Allow(ctx){startAdd(1)}}func(c*StartCounter)Dec(ctx*via.Ctx){ifc.Lim.Allow(ctx){startAdd(-1)}}func(c*StartCounter)View()h.H{returnh.Div(h.Class("row"),h.Button(on.Click(c.Dec),h.Str("−")),h.Output(c.N.Display()),h.Button(on.Click(c.Inc),h.Str("+")),)}
Add a Reset method that stores 0 and publishes it, and a third button with on.Click(c.Reset). Every tab drops to 0.
Rename c.Inc to c.Increment in View only. go build stops with c.Increment undefined (type *Counter has no field or method Increment): an action is a method value, not a string.