Preserving File Inputs

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.

Preserving Form Inputs

Template

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}}

preserve-inputs.tmpl

Handler & state

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",
	}))
}

handlers_forms.go:227-257

type PreserveInputsState struct {
	Title       string
	Category    string
	Name        string
	Description string
}

state_forms.go:70-76

When to use

source: livetemplate/docs · path: examples/patterns/templates/forms/preserve-inputs.tmpl