Forms and validation
Forms declare the fields you accept, bind a submission and validate it. Use one
for any browser form, like a Django Form.
Act on the data only after validation succeeds. Keep session and CSRF middleware around the handler.
#Ask your agent
Read https://cicadel.fauzaanu.com/en/0.2.7/topics/forms-completion/index.md.
Add a form for [fields and validation rules] to my application.
Show field errors beside the controls and preserve non-sensitive submitted values.
Use the existing sessions and CSRF middleware. Test valid input, invalid input,
missing CSRF tokens and unexpected fields before connecting any side effects.#Define fields
Create the definition once, during application setup. Handle configuration errors before you serve requests.
package contact
import "github.com/fauzaanu/cicadel/forms"
func Definition() (*forms.Definition, error) {
return forms.New([]forms.Field{
forms.Text(forms.Options{
Name: "email", Label: "Email", Widget: "email", Required: true,
}, forms.Email),
forms.Boolean(forms.Options{
Name: "accepted", Label: "I agree to the terms",
}, forms.Accepted),
})
}Only declared fields are bound. For other common inputs, use forms.Integer,
forms.Number, forms.Date, forms.Select and forms.Multiple.
#Bind a submission
In your POST handler, after authentication and CSRF checks:
r.Body = http.MaxBytesReader(w, r.Body, 8<<10)
posted, err := forms.Parse(r)
if err != nil {
response.Error(w, http.StatusBadRequest)
return
}
bound := definition.Bind(posted.Values)
if !bound.Valid() {
// Render your form page with bound and status 422.
renderForm(w, r, http.StatusUnprocessableEntity, bound)
return
}
email, ok := forms.Value[string](bound, "email")
if !ok {
response.Error(w, http.StatusInternalServerError)
return
}
// Use email for the application's next step only after validation succeeds.definitionis the result ofDefinition().renderFormis your page-rendering helper.- For a GET, render
definition.Initial()instead.
Warning
Do not log submitted values or redisplay passwords.
#Render labels and errors
This component renders the bound fields. Pass a CSRF token from your existing session middleware.
package contact
import "github.com/fauzaanu/cicadel/forms"
import "github.com/fauzaanu/cicadel/ui"
import "github.com/fauzaanu/cicadel/ui/form"
templ fields(bound *forms.Bound, csrfToken string) {
<form method="post" action="/contact">
@form.CSRF.Call(form.CSRFProps{Token: csrfToken}, ui.NoSlots{})
for _, field := range bound.Fields() {
@form.Field.Call(form.Bound(field, "contact-" + field.Name), ui.NoSlots{})
}
<button type="submit">Submit</button>
</form>
}The field component connects labels, help text and errors to the control. Show
form-wide errors separately with bound.Errors()[""].
The component guide shows a complete handler and page-rendering flow.
#Save a model
Use model forms when accepted values create or update a GORM model. Define an allowlist of editable fields. Scope every update to records the current user may change.
#Stale edits
Reject an edit when someone else changed the record first:
- Add a
Revision int64column with a recorded migration. - Set
model.Config[T].VersionFieldto"Revision". - Render
form.Version(ctx, &record)in a hidden_versionfield. - When a save returns
model.ErrConflict, show a conflict message. Ask the user to reload before trying again.
#File fields and persistence
Use forms/upload for bounded multipart uploads.
- Close each parsed batch.
- Choose trusted storage keys.
- Connect storage writes to your persistence operation.
The upload reference covers limits, validation, cleanup and middleware order.
#Admin integration
- Use
admin.Config[T].Modelto supply model-form fields and validation. - Use the first-party formsets module for multiple rows and inline editing.
See the forms reference for field types, custom validators and transaction behavior.