Value Select
Cascading dependent selects. The Change() handler auto-fires when the Make select changes and updates the Model options server-side — no client JS.
Picking a Make repopulates the Model select server-side. The Change handler
auto-fires when the make select changes, looks up that make's models, and
auto-selects the first one so the cascade is visible — no client JS. The Model select
stays disabled until a Make is chosen, and Mount seeds the make list (and any
pre-selected models) on connect.
Both <select> elements live in one form; Change fires on either. The Model
options come from .Models, which the server refills whenever make changes.
{{define "content"}}
<article>
<h3>Value Select</h3>
<p><small>Cascading dependent selects. The Change() handler auto-fires when the Make select changes and updates the Model options server-side — no client JS.</small></p>
<form method="POST">
<label>Make
<select name="make">
<option value="">Select Make</option>
{{range .Makes}}
<option value="{{.}}" {{if eq $.Make .}}selected{{end}}>{{.}}</option>
{{end}}
</select>
</label>
<label>Model
<select name="model" {{if not .Make}}disabled{{end}}>
<option value="">Select Model</option>
{{range .Models}}
<option value="{{.}}" {{if eq $.Model .}}selected{{end}}>{{.}}</option>
{{end}}
</select>
</label>
</form>
{{if .Model}}
<p>Selected: <strong>{{.Make}} {{.Model}}</strong></p>
{{end}}
</article>
{{end}}
Change checks which field changed: a new make reloads Models and resets the
selection; a new model just records it. Mount populates the initial lists.
type ValueSelectController struct{}
func (c *ValueSelectController) Mount(state ValueSelectState, ctx *livetemplate.Context) (ValueSelectState, error) {
state.Makes = getCarMakes()
if state.Make != "" {
state.Models = getCarModels(state.Make)
}
return state, nil
}
func (c *ValueSelectController) Change(state ValueSelectState, ctx *livetemplate.Context) (ValueSelectState, error) {
if ctx.Has("make") {
state.Make = ctx.GetString("make")
state.Models = getCarModels(state.Make)
// Auto-select first model so the user sees the cascade propagate.
state.Model = ""
if len(state.Models) > 0 {
state.Model = state.Models[0]
}
}
if ctx.Has("model") {
state.Model = ctx.GetString("model")
}
return state, nil
}
func valueSelectHandler() http.Handler {
tmpl := newLayoutTmpl("templates/layout.tmpl", "templates/lists/value-select.tmpl")
return tmpl.Handle(&ValueSelectController{}, livetemplate.AsState(&ValueSelectState{
Title: "Value Select",
Category: "Lists & Data",
}))
}
type ValueSelectState struct {
Title string
Category string
Makes []string
Models []string
Make string
Model string
}
For a non-cascading list that grows on demand, see Click To Load.