One form, many rows of checkboxes, a single submit. Each checkbox is named
active-<id>; the handler walks the rows, applies the new states, counts what
changed, and sets a flash summarizing the result.
The checkbox name encodes the row ID, so one form submits every row's state at once.
{{define "content"}}
<article>
<h3>Bulk Update</h3>
<form method="POST">
<table>
<thead><tr><th>Name</th><th>Email</th><th>Active</th></tr></thead>
<tbody>
{{range .Users}}
<tr data-key="{{.ID}}">
<td>{{.Name}}</td>
<td>{{.Email}}</td>
<td><input type="checkbox" name="active-{{.ID}}" aria-label="Active for {{.Name}}" {{if .Active}}checked{{end}}></td>
</tr>
{{end}}
</tbody>
</table>
<button name="bulkUpdate">Update</button>
</form>
{{.lvt.FlashTag "success"}}
{{.lvt.FlashTag "info"}}
</article>
{{end}}
BulkUpdate reads each active-<id> with ctx.GetBool, applies it, and flashes a
count (or "No changes").
type BulkUpdateController struct{}
func (c *BulkUpdateController) BulkUpdate(state BulkUpdateState, ctx *livetemplate.Context) (BulkUpdateState, error) {
changed := 0
for i, user := range state.Users {
newActive := ctx.GetBool("active-" + user.ID)
if newActive != user.Active {
changed++
}
state.Users[i].Active = newActive
}
if changed == 0 {
ctx.ClearFlash("success")
ctx.SetFlash("info", "No changes")
} else {
ctx.ClearFlash("info")
ctx.SetFlash("success", fmt.Sprintf("Updated %d user(s)", changed))
}
return state, nil
}
func bulkUpdateHandler() http.Handler {
tmpl := newLayoutTmpl("templates/layout.tmpl", "templates/forms/bulk-update.tmpl")
return tmpl.Handle(&BulkUpdateController{}, livetemplate.AsState(&BulkUpdateState{
Title: "Bulk Update",
Category: "Forms & Editing",
Users: sampleUsers(),
}))
}
type BulkUpdateState struct {
Title string
Category string
Users []UserRow
}
name="field-<id>" convention scales to any number of rows with no extra
client code.