A file input can't be re-populated by the server for security reasons, so a naive
re-render would wipe the user's selection. lvt-form:preserve on the <form> tells
the client to retain the live form values (including the chosen file) across a
re-render, so a validation error on another field doesn't lose the attachment.
One attribute — lvt-form:preserve on the form — keeps the inputs across re-renders.
{{define "content"}}
<article>
<h3>Preserving Form Inputs</h3>
<form method="POST" lvt-form:preserve>
<fieldset>
<label>Name
<input name="name" value="{{.Name}}" required>
</label>
<label>Description
<textarea name="description">{{.Description}}</textarea>
</label>
<label>Attachment
<input type="file" name="attachment">
</label>
<button type="submit">Submit</button>
</fieldset>
</form>
{{.lvt.FlashTag "success"}}
{{.lvt.ErrorTag "name"}}
</article>
{{end}}
Submit validates and flashes; on a validation error the form re-renders but the
client-preserved file stays selected.
type PreserveInputsController struct{}
func (c *PreserveInputsController) Submit(state PreserveInputsState, ctx *livetemplate.Context) (PreserveInputsState, error) {
state.Name = ctx.GetString("name")
state.Description = ctx.GetString("description")
if err := ctx.ValidateForm(); err != nil {
return state, err
}
ctx.SetFlash("success", "Saved: "+state.Name, livetemplate.FlashExpiry(flashSuccessExpiry))
nudgeFlashExpiry(ctx, flashSuccessExpiry)
return state, nil
}
func (c *PreserveInputsController) Refresh(state PreserveInputsState, ctx *livetemplate.Context) (PreserveInputsState, error) {
return state, nil
}
func preserveInputsHandler() http.Handler {
tmpl := newLayoutTmplWithOpts(
[]string{"templates/layout.tmpl", "templates/forms/preserve-inputs.tmpl"},
livetemplate.WithUpload("attachment", livetemplate.UploadConfig{
MaxFileSize: 10 << 20, // 10 MB
MaxEntries: 1,
}),
)
return tmpl.Handle(&PreserveInputsController{}, livetemplate.AsState(&PreserveInputsState{
Title: "Preserving File Inputs",
Category: "Forms & Editing",
}))
}
type PreserveInputsState struct {
Title string
Category string
Name string
Description string
}