# `MishkaGervaz.Form.Web.Events.SubmitHandler`
[🔗](https://github.com/mishka-group/mishka_gervaz/blob/v0.0.2/lib/mishka_gervaz/form/web/events/submit_handler.ex#L1)

Handles form submission (phx-submit events).

## Overridable Functions

- `submit/3` - Submit the form (create or update)
- `transform_params/2` - Transform params before submission
- `after_save/3` - Handle post-save logic

## User Override

    defmodule MyApp.Form.SubmitHandler do
      use MishkaGervaz.Form.Web.Events.SubmitHandler

      def transform_params(state, params) do
        params |> super(state) |> Map.put("custom_field", "value")
      end
    end

Top-level helpers (`format_form_errors/1`, `extract_form_level_errors/2`,
`save_errors/2`, `cleanup_temp_uploads/1`, `push_js_hook/4`, `merge_defaults/2`,
`drop_protected_fields/2`, `field_protected?/2`, `field_restricted?/2`, `field_readonly?/2`)
are public so user overrides can reuse them — they live outside the
`__using__` macro to avoid per-consumer compile cost.

See `MishkaGervaz.Form.Web.Events`,
`MishkaGervaz.Form.Web.Events.Helpers` (for `parse_typed_params/2` and
`merge_uploaded_files/4`),
`MishkaGervaz.Form.Web.DataLoader`,
`MishkaGervaz.Form.Web.UploadHelpers`, and the sibling sub-handlers.

# `extract_form_level_errors`

```elixir
@spec extract_form_level_errors(Phoenix.HTML.Form.t(), MapSet.t(), module() | nil) ::
  [String.t()]
```

The errors of `form` on fields not in `field_names`, each as `MishkaGervaz.Errors.field_message/4`
words it, its field named by the labels `resource` declares.

# `field_protected?`

```elixir
@spec field_protected?(map(), map()) :: boolean()
```

Whether the current user may not change `field`: it is `restricted` to masters and the user is
not one, or `field_readonly?/2` is true.

A save leaves such a field out, and `MishkaGervaz.Form.Web.Events` ignores a `relation_select`,
`relation_toggle`, `relation_clear`, `combobox_select`, `field_change` or calendar `picker_*`
event sent for it.

# `format_form_errors`

```elixir
@spec format_form_errors(Phoenix.HTML.Form.t()) :: map()
```

The errors of `form` by field, each message translated with
`MishkaGervaz.Errors.translate_error/2`.

# `merge_defaults`

```elixir
@spec merge_defaults(map(), map()) :: map()
```

Fills each param a create form left `nil` or `""` from `state.defaults`.

A default is left out when its field no longer holds, in `state.field_values`, the value the form
was built with. A picker cleared on screen, or emptied because a picker it depends on changed, is
saved empty rather than with its default. Update forms are returned unchanged.

# `save_errors`

```elixir
@spec save_errors(AshPhoenix.Form.t(), MapSet.t()) :: {map(), [String.t()]}
```

The errors a failed save shows, as `{field_errors, form_errors}`.

`field_errors` maps a field name to its messages and `form_errors` lists the messages no field
shows. An error under a field that has no nested form, such as one id of a relation field's
`[:tag_ids, 0]`, is shown on that field. When the save failed and none of its errors can be shown,
`form_errors` holds one message saying the changes were not saved.

A message is translated with `MishkaGervaz.Errors.translate_error/2`. One in `form_errors` about
a field is worded by `MishkaGervaz.Errors.field_message/4`, with the form's resource naming the
field.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
