# Forms and validation

Define allowed fields, bind the submitted values, and act only after validation succeeds. For browser forms, keep session and CSRF middleware around the handler.

## Ask your agent

```text
Read https://cicadel.fauzaanu.com/en/dev/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 serving requests.

<!-- example: forms/apps/contact/form.go -->
```go
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. `forms.Integer`, `forms.Number`, `forms.Date`, `forms.Select`, and `forms.Multiple` cover other common inputs.

## Bind a submission

In your POST handler, after authentication and CSRF checks:

```go
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.
```

Here `definition` is the result of `Definition()`, and `renderForm` is your page-rendering helper. For a GET, render `definition.Initial()` instead. Do not log submitted values or redisplay passwords.

## Render labels and errors

This component renders the bound fields. Pass a CSRF token obtained from your existing session middleware.

<!-- example: forms/apps/contact/form.templ -->
```templ
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. Display form-wide errors separately using `bound.Errors()[""]`. The [component guide](https://cicadel.fauzaanu.com/en/dev/howto/component-authoring/index.md) shows a complete handler and page-rendering flow.

## Save a model

Use [model forms](https://cicadel.fauzaanu.com/en/dev/topics/model-forms/index.md) when accepted values should create or update a GORM model. Define an editable-field allowlist and scope every update to records the current user may change.

## Stale edits

For editable records, add a `Revision int64` column with a recorded migration, set `model.Config[T].VersionField` to `"Revision"`, and render `form.Version(ctx, &record)` in a hidden `_version` field. Return a conflict message when a save returns `model.ErrConflict`; 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, and connect storage writes to your persistence operation. The [upload reference](https://cicadel.fauzaanu.com/en/dev/sources/docs/reference/forms.md#file-fields-and-persistence) covers limits, validation, cleanup and middleware order.

## Admin integration

Use `admin.Config[T].Model` to supply model-form fields and validation. Use the first-party [formsets module](https://cicadel.fauzaanu.com/en/dev/ref/formsets/index.md) for multiple rows and inline editing. See the [forms reference](https://cicadel.fauzaanu.com/en/dev/sources/docs/reference/forms.md) for field types, custom validators and transaction behavior.
