astwerk

Two-way form bindings

Two number inputs bound two-way, a total derived from them, and a checkbox revealing a panel. Nothing here talks to the DOM directly except the bindings — the compiled-markup version is Forms & computed.

Total: 20.00

How it works

State

go
price := reactive.NewSignal(10.0)
qty := reactive.NewSignal(2.0)
total := reactive.Computed(func() float64 { return price.Get() * qty.Get() })
tax := reactive.NewSignal(false)

Two signals hold the inputs; Computed derives the total from them — calling price.Get() inside the function is what registers the dependency, so the total recomputes whenever either input changes.

The inputs

go
reactive.BindNumber(root.Find("[data-price]"), price)
reactive.BindNumber(root.Find("[data-qty]"), qty)
reactive.BindText(root.Find("[data-total]"), func() string {
	return fmt.Sprintf("%.2f", total.Get())
})

BindNumber is two-way — typing writes the signal, setting the signal writes the field — and input that isn't a number is ignored. The total is a text binding over the memo.

The checkbox

go
reactive.BindChecked(root.Find("[data-tax]"), tax)
reactive.BindShow(root.Find("[data-tax-note]"), tax.Get)

BindChecked two-way binds a bool, and BindShow lets that same signal drive a panel's visibility — the note appears and disappears, nothing is rebuilt.

Full source
go
price := reactive.NewSignal(10.0)
qty := reactive.NewSignal(2.0)
total := reactive.Computed(func() float64 { return price.Get() * qty.Get() })
tax := reactive.NewSignal(false)

reactive.Hydrate("#form-demo", func(root wasmwrap.Element) {
	reactive.BindNumber(root.Find("[data-price]"), price)
	reactive.BindNumber(root.Find("[data-qty]"), qty)
	reactive.BindText(root.Find("[data-total]"), func() string {
		return fmt.Sprintf("%.2f", total.Get())
	})
	reactive.BindChecked(root.Find("[data-tax]"), tax)
	reactive.BindShow(root.Find("[data-tax-note]"), tax.Get)
})
How this demo runs

scripts/demo.go is compiled to demo.wasm by CompileFrom and loaded by WasmScript — see Scripts & WebAssembly. Without WebAssembly the page still renders; only the bindings stay put.