# 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

```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 you serve 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. 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:

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

- `definition` is the result of `Definition()`.
- `renderForm` is 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.

<!-- 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. Show
form-wide errors separately with `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 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:

1. Add a `Revision int64` column with a recorded migration.
2. Set `model.Config[T].VersionField` to `"Revision"`.
3. Render `form.Version(ctx, &record)` in a hidden `_version` field.
4. 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](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.
