# `AshPhoenix.Form`
[🔗](https://github.com/ash-project/ash_phoenix/blob/v2.3.21/lib/ash_phoenix/form/form.ex#L5)

A module to allow you to fluidly use resources with Phoenix forms.

### Life cycle

The general workflow is, with either LiveView or Phoenix forms:

1. Create a form with `AshPhoenix.Form`
2. Render the form with `Phoenix.Component.form` (or `CoreComponents.simple_form`), or, if using Surface, `<Form>`
3. To validate the form (e.g with `phx-change` for liveview), pass the submitted params to `AshPhoenix.Form.validate/3`
4. On form submission, pass the params to `AshPhoenix.Form.submit/2`
5. On success, use the result to redirect or assign. On failure, reassign the provided form.

The following keys exist on the form to show where in the lifecycle you are:

- `submitted_once?` - If the form has ever been submitted. Useful for not showing any errors on the first attempt to fill out a form.
- `just_submitted?` - If the form has just been submitted and *no validation* has happened since. Useful for things like
  triggering a UI effect that should stop when the form is modified again.
- `.changed?` - If something about the form is different than it originally was. Note that in some cases this can yield a
  false positive, specifically if a nested form is removed and then a new one is added with the exact same values.
- `.touched_forms` - A MapSet containing all keys in the form that have been modified. When submitting a form, only these keys are included in the parameters.

### Forms in the code interface

Throughout this documentation you will see forms created with `AshPhoenix.Form.for_create/3` and other functions like it.
This is perfectly fine to do, however there is a way to use `AshPhoenix.Form` in a way that adds clarity to its usage
and makes it easier to find usage of each action. Code interfaces allow us to do this for standard action calls, i.e:

```elixir
resources do
  resource MyApp.Accounts.User do
    define :register_with_password, args: [:email, :password]
    define :update_user, action: :update, args: [:email, :password]
  end
end
```

Adding the `AshPhoenix` extension to our domains and resources, like so:

```elixir
use Ash.Domain,
  extensions: [AshPhoenix]
```

will cause another function to be generated for each definition, beginning with `form_to_`.

By default, the `args` option in `define` is ignored when building forms. If you want to have
positional arguments, configure that in the `forms` section which is added by the `AshPhoenix`
section. For example:

```elixir
forms do
  form :update_user, args: [:email]
end
```

With this extension, the standard setup for forms looks something like this:

```elixir
def render(assigns) do
  ~H"""
  <.form for={@form} phx-change="validate" phx-submit="submit">
    <.input field={@form[:email]} />
    <.input field={@form[:password]} />
    <.button type="submit" />
  </.form>
  """
end

def mount(_params, _session, socket) do
  # Here we call our new generated function to create the form
  {:ok, assign(socket, form: MyApp.Accounts.form_to_register_with_password() |> to_form())}
end

def handle_event("validate", %{"form" => params}, socket) do
  form = AshPhoenix.Form.validate(socket.assigns.form, params)
  {:noreply, assign(socket, :form, form)}
end

def handle_event("submit", %{"form" => params}, socket) do
  case AshPhoenix.Form.submit(socket.assigns.form, params: params) do
    {:ok, _user} ->
      socket =
        socket
        |> put_flash(:success, "User registered successfully")
        |> push_navigate(to: ~p"/")

      {:noreply, socket}

    {:error, form} ->
      socket =
        socket
        |> put_flash(:error, "Something went wrong")
        |> assign(:form, form)

      {:noreply, socket}
  end
end
```

## Working with related or embedded data

See the [nested forms guide](/documentation/topics/nested-forms.md)

## Working with compound types
Compound types, such as `Ash.Money`, will need some extra work to make it work.

For instance, when working with the `Transfer` type in `AshDoubleEntry.Transfer`, it will have the `Ash.Money` type for `amount`. When rendering the forms, you should do as follows:
```
<.input
    name={@form[:amount].name <> "[amount]"}
    id={@form[:amount].id <> "_amount"}
    label="Amount"
    value={if(@form[:amount].value, do: @form[:amount].value.amount)}
  />
  <.input
    type="select"
    name={@form[:amount].name <> "[currency]"}
    id={@form[:amount].id <> "_currency"}
    options={[:USD, :HKD, :EUR]}
    label="Currency"
    value={if(@form[:amount].value, do: @form[:amount].value.currency)}
  />
```

### Handling errors for composite inputs

When working with composite inputs like the example above, you may need to map errors from the composite field
to the individual input fields. The `post_process_errors` option can help with this:

```elixir
AshPhoenix.Form.for_create(Transfer, :create,
  post_process_errors: fn _form, _path, {field, message, vars} ->
    # Map amount field errors to the amount input specifically
    case field do
      :amount ->
        # Could check the error message to determine which sub-field
        if String.contains?(message, "currency") do
          {:amount_currency, message, vars}
        else
          {:amount_amount, message, vars}
        end
      _ ->
        # you can return `nil`
        {field, message, vars}
    end
  end
)
```

The above will allow the fields to be used by the `AshPhoenix.Form` when creating or updating a Transfer.
You can follow the same style with other compound types.

# `path`

```elixir
@type path() :: String.t() | atom() | [String.t() | atom() | integer()]
```

# `source`

```elixir
@type source() :: Ash.Changeset.t() | Ash.Query.t() | Ash.Resource.record()
```

# `t`

```elixir
@type t() :: %AshPhoenix.Form{
  action: atom(),
  added?: term(),
  any_removed?: term(),
  changed?: term(),
  data: nil | Ash.Resource.record(),
  domain: term(),
  errors: boolean(),
  form_keys: Keyword.t(),
  forms: map(),
  id: term(),
  just_submitted?: boolean(),
  method: String.t(),
  name: term(),
  opts: Keyword.t(),
  original_data: term(),
  params: map(),
  post_process_errors:
    nil
    | (t(),
       [String.t() | atom()],
       {field :: atom(), message :: String.t(), vars :: Keyword.t()} -&gt;
         {field :: atom(), message :: String.t(), vars :: Keyword.t()} | nil),
  prepare_params: term(),
  prepare_source: nil | (source() -&gt; source()),
  raw_params: term(),
  resource: Ash.Resource.t(),
  source: source(),
  submit_errors: Keyword.t() | nil,
  submitted_once?: boolean(),
  touched_forms: term(),
  transform_errors:
    nil
    | (source(), error :: Ash.Error.t() -&gt;
         [
           {field :: atom(), message :: String.t(),
            substituations :: Keyword.t()}
         ]),
  transform_params:
    nil | (map(), atom() -&gt; term()) | (t(), map(), atom() -&gt; term()),
  type: :create | :update | :destroy | :read,
  valid?: boolean(),
  warn_on_unhandled_errors?: term()
}
```

# `add_error`

Adds an error to the source underlying the form.

This can be used for adding errors from different sources to a form. Keep in mind, if they don't match
a field on the form (typically extracted via the `field` key in the error), they won't be displayed by default.
Ensure that the `errors` field of the form is set to `true` if you want the errors to be visible.

See `Ash.Error.to_ash_error/3` for more on supported values for `error`.

# Options

- `:path` - The path to add the error to. If the error(s) already have a path, don't specify a path yourself.

# `add_form`

```elixir
@spec add_form(t(), path(), Keyword.t()) :: t()
@spec add_form(Phoenix.HTML.Form.t(), path(), Keyword.t()) :: Phoenix.HTML.Form.t()
```

Adds a new form at the provided path.

Doing this requires that the form has a `create_action` and a `resource` configured.

`path` can be one of two things:

1. A list of atoms and integers that lead to a form in the `forms` option provided. `[:posts, 0, :comments]` to add a comment to the first post.
2. The html name of the form, e.g `form[posts][0][comments]` to mimic the above

If you pass parameters to this function, keep in mind that, unless they are string keyed in
the same shape they might come from your form, then the result of `params/1` will reflect that,
i.e `add_form(form, "foo", params: %{bar: 10})`, could produce params like `%{"field" => value, "foo" => [%{bar: 10}]}`.
Notice how they are not string keyed as you would expect. However, once the form is changed (in liveview) and a call
to `validate/2` is made with that input, then the parameters would become what you'd expect. In this way, if you are using
`add_form` with not string keys/values you may not be able to depend on the shape of the `params` map (which you should ideally
not depend on anyway).

* `:prepend` (`t:boolean/0`) - If specified, the form is placed at the beginning of the list instead of the end of the list The default value is `false`.

* `:params` (`t:term/0`) - The initial parameters to add the form with. The default value is `%{}`.

* `:validate?` (`t:boolean/0`) - Validates the new full form. The default value is `true`.

* `:validate_opts` (`t:term/0`) - Options to pass to `validate`. Only used if `validate?` is set to `true` (the default) The default value is `[]`.

* `:type` - If `type` is set to `:read`, the form will be created for a read action. A hidden field will be set in the form called `_form_type` to track this information. Valid values are :read, :create, :update, :destroy The default value is `:create`.

* `:data` (`t:term/0`) - The data to set backing the form. Generally you'd only want to do this if you are adding a form with `type: :read` additionally.

# `arguments`

A utility to get the list of arguments the action underlying the form accepts

# `attributes`

A utility to get the list of attributes the action underlying the form accepts

# `can_submit?`

```elixir
@spec can_submit?(t()) :: boolean()
@spec can_submit?(Phoenix.HTML.Form.t()) :: boolean()
```

# `clear_value`

```elixir
@spec clear_value(t(), atom() | [atom()]) :: t()
```

Clears a given input's value on a form.

Accepts a field (atom) or a list of fields (atoms) as a second argument.

# `do_sort_forms`

# `ensure_can_submit!`

```elixir
@spec ensure_can_submit!(t()) :: t()
@spec ensure_can_submit!(Phoenix.HTML.Form.t()) :: Phoenix.HTML.Form.t()
```

# `errors`

```elixir
@spec errors(t() | Phoenix.HTML.Form.t(), Keyword.t()) ::
  ([{atom(), {String.t(), Keyword.t()}}]
   | [String.t()]
   | [{atom(), String.t()}])
  | %{
      required(list()) =&gt;
        [{atom(), {String.t(), Keyword.t()}}]
        | [String.t()]
        | [{atom(), String.t()}]
    }
```

Returns the errors on the form, sanitized for displaying to the end user.

By default, only errors on the form being passed in (not nested forms) are provided.
Use `for_path` to get errors for nested forms.

* `:format` - Values:
      - `:raw` - `[field:, {message, substitutions}}]` (for translation)
      - `:simple` - `[field: "message w/ variables substituted"]`
      - `:plaintext` - `["field: message w/ variables substituted"]`
   Valid values are :simple, :raw, :plaintext The default value is `:simple`.

* `:for_path` (`t:term/0`) - The path of the form you want errors for, either as a list or as a string, e.g `[:comments, 0]` or `form[comments][0]`
  Passing `:all` will cause this function to return a map of path to its errors, like so:  
  `%{[:comments, 0] => [body: "is invalid"], ...}` The default value is `[]`.

# `for_action`

Creates a form corresponding to any given action on a resource.

If given a create, read, update, or destroy action, the appropriate `for_*`
function will be called instead. So use this function when you don't know
the type of the action, or it is a generic action.

## Options

* `:actor` (`t:term/0`) - The actor performing the action. Passed through to the underlying action.

* `:scope` (`t:term/0`) - A value that implements the `Ash.Scope` protocol, for passing around actor/tenant/context in a single value. See `Ash.Scope` for more.

* `:forms` (`t:keyword/0`) - Nested form configurations. See `for_create/3` "Nested Form Options" docs for more.

* `:warn_on_unhandled_errors?` (`t:boolean/0`) - Warns on any errors that don't match the form pattern of `{:field, "message", [replacement: :vars]}` or implement the `AshPhoenix.FormData.Error` protocol. The default value is `true`.

* `:domain` (`t:atom/0`) - The domain to use when calling the action.

* `:as` (`t:String.t/0`) - The name of the form in the submitted params. You will need to pull the form params out using this key. The default value is `"form"`.

* `:id` (`t:String.t/0`) - The html id of the form. Defaults to the value of `:as` if provided, otherwise "form"

* `:transform_errors` - Allows for manual manipulation and transformation of errors. You may prefer `post_process_errors` as a simpler API.  
  If possible, try to implement `AshPhoenix.FormData.Error` for the error (if it as a custom one, for example).
  If that isn't possible, you can provide this function which will get the changeset and the error, and should
  return a list of ash phoenix formatted errors, e.g `[{field :: atom, message :: String.t(), substituations :: Keyword.t()}]`  
  Example:
  ```
  AshPhoenix.Form.for_create(..., transform_errors: fn
    _changeset, %{field: :field1} = error ->
      %{error | field: :field2}  
    _changeset, error ->
      error
  end
  ```

* `:post_process_errors` - Allows for post-processing of errors after they have been converted to the standard triple format.  
  This function receives the form, the path to the form, and an error triple `{field, message, vars}`.
  It should return either a modified triple or `nil` to filter out the error.  
  This is useful for:
  * Filtering out certain errors based on custom criteria
  * Remapping field names from one field to another
  * Modifying error messages or variables  
  Example:
  ```
  AshPhoenix.Form.for_create(..., post_process_errors: fn form, _path, {field, message, vars} ->
    case field do
      :status ->
        # hide these errors
        nil  
      field when field in [:currency, :amount] ->
        # remap the field, and replace the error message
        {:money, "please enter a real money amount", []}  
      field ->
        # leave the others unchanged
        {field, message, vars}
    end
  end)
  ```

* `:prepare_source` - A 1-argument function the receives the initial changeset (or query) and makes any relevant changes to it.
  This can be used to do things like:  
  * Set default argument values before the validations are run using `Ash.Changeset.set_arguments/2` or `Ash.Changeset.set_argument/3`
  * Set changeset context
  * Do any other pre-processing on the changeset

* `:prepare_params` - A 2-argument function that receives the params map and the :validate atom and should return prepared params.
  Called before the form is validated.

* `:transform_params` - A function for post-processing the form parameters before they are used for changeset validation/submission.
  Use a 3-argument function to pattern match on the `AshPhoenix.Form` struct.

* `:method` (`t:String.t/0`) - The http method to associate with the form. Defaults to `post` for creates, and `put` for everything else.

* `:exclude_fields_if_empty` - These fields will be ignored if they are empty strings.  
  This list of fields supports dead view forms. When a form is submitted from dead view
  empty fields are submitted as empty strings. This is problematic for fields that allow_nil
  or those that have default values.

* `:tenant` (`t:term/0`) - The current tenant. Passed through to the underlying action.

* `:params` (`t:term/0`) - The initial parameters to use for the form. This is useful for setting up a form with default values. The default value is `%{}`.

Any *additional* options will be passed to the underlying call to build the source, i.e
`Ash.ActionInput.for_action/4`, or `Ash.Changeset.for_*`. This means you can set things
like the tenant/actor. These will be retained, and provided again when `Form.submit/3` is called.

## Nested Form Options

* `:type` - The cardinality of the nested form - `:list` or `:single`. Valid values are :list, :single The default value is `:single`.

* `:sparse?` (`t:boolean/0`) - If the nested form is `sparse`, the form won't expect all inputs for all forms to be present.  
  Has no effect if the type is `:single`.  
  Normally, if you leave some forms out of a list of nested forms, they are removed from the parameters
  passed to the action. For example, if you had a `post` with two comments `[%Comment{id: 1}, %Comment{id: 2}]`
  and you passed down params like `comments[0][id]=1&comments[1][text]=new_text`, we would remove the second comment
  from the input parameters, resulting in the following being passed into the action: `%{"comments" => [%{"id" => 1, "text" => "new"}]}`.
  By setting it to sparse, you have to explicitly use `remove_form` for that removal to happen. So in the same scenario above, the parameters
  that would be sent would actually be `%{"comments" => [%{"id" => 1, "text" => "new"}, %{"id" => 2}]}`.  
  One major difference with `sparse?` is that the form actually ignores the *index* provided, e.g `comments[0]...`, and instead uses the primary
  key e.g `comments[0][id]` to match which form is being updated. This prevents you from having to find the index of the specific item you want to
  update. Which could be very gnarly on deeply nested forms. If there is no primary key, or the primary key does not match anything, it is treated
  as a new form.  
  REMEMBER: You need to use `Phoenix.Components.inputs_for` to render the nested forms, or manually add hidden inputs using
  `hidden_inputs_for` (or `HiddenInputs` if using Surface) for the id to be automatically placed into the form.

* `:forms` (`t:keyword/0`) - Forms nested inside the current nesting level in all cases.

* `:for_type` - What action types the form applies for. Leave blank for it to apply to all action types. Valid values are :read, :create, :update

* `:merge?` (`t:boolean/0`) - When building parameters, this input will be merged with its parent input. This allows for combining multiple forms into a single input. The default value is `false`.

* `:for` (`t:atom/0`) - When creating parameters for the action, the key that the forms should be gathered into. Defaults to the key used to configure the nested form. Ignored if `merge?` is `true`.

* `:resource` (`t:atom/0`) - The resource of the nested forms. Unnecessary if you are providing the `data` key, and not adding additional forms to this path.

* `:create_action` (`t:atom/0`) - The create action to use when building new forms. Only necessary if you want to use `add_form/3` with this path.

* `:update_action` (`t:atom/0`) - The update action to use when building forms for data. Only necessary if you supply the `data` key.

* `:data` (`t:term/0`) - The current value or values that should have update forms built by default.  
  You can also provide a single argument function that will return the data based on the
  data of the parent form. This is important for multiple nesting levels of `:list` type
  forms, because the data depends on which parent is being rendered.

# `for_create`

```elixir
@spec for_create(Ash.Resource.t(), action :: atom(), opts :: Keyword.t()) :: t()
```

Creates a form corresponding to a create action on a resource.

## Options

Options not listed below are passed to the underlying call to build the changeset/query, i.e `Ash.Changeset.for_create/4`

* `:actor` (`t:term/0`) - The actor performing the action. Passed through to the underlying action.

* `:scope` (`t:term/0`) - A value that implements the `Ash.Scope` protocol, for passing around actor/tenant/context in a single value. See `Ash.Scope` for more.

* `:forms` (`t:keyword/0`) - Nested form configurations. See `for_create/3` "Nested Form Options" docs for more.

* `:warn_on_unhandled_errors?` (`t:boolean/0`) - Warns on any errors that don't match the form pattern of `{:field, "message", [replacement: :vars]}` or implement the `AshPhoenix.FormData.Error` protocol. The default value is `true`.

* `:domain` (`t:atom/0`) - The domain to use when calling the action.

* `:as` (`t:String.t/0`) - The name of the form in the submitted params. You will need to pull the form params out using this key. The default value is `"form"`.

* `:id` (`t:String.t/0`) - The html id of the form. Defaults to the value of `:as` if provided, otherwise "form"

* `:transform_errors` - Allows for manual manipulation and transformation of errors. You may prefer `post_process_errors` as a simpler API.  
  If possible, try to implement `AshPhoenix.FormData.Error` for the error (if it as a custom one, for example).
  If that isn't possible, you can provide this function which will get the changeset and the error, and should
  return a list of ash phoenix formatted errors, e.g `[{field :: atom, message :: String.t(), substituations :: Keyword.t()}]`  
  Example:
  ```
  AshPhoenix.Form.for_create(..., transform_errors: fn
    _changeset, %{field: :field1} = error ->
      %{error | field: :field2}  
    _changeset, error ->
      error
  end
  ```

* `:post_process_errors` - Allows for post-processing of errors after they have been converted to the standard triple format.  
  This function receives the form, the path to the form, and an error triple `{field, message, vars}`.
  It should return either a modified triple or `nil` to filter out the error.  
  This is useful for:
  * Filtering out certain errors based on custom criteria
  * Remapping field names from one field to another
  * Modifying error messages or variables  
  Example:
  ```
  AshPhoenix.Form.for_create(..., post_process_errors: fn form, _path, {field, message, vars} ->
    case field do
      :status ->
        # hide these errors
        nil  
      field when field in [:currency, :amount] ->
        # remap the field, and replace the error message
        {:money, "please enter a real money amount", []}  
      field ->
        # leave the others unchanged
        {field, message, vars}
    end
  end)
  ```

* `:prepare_source` - A 1-argument function the receives the initial changeset (or query) and makes any relevant changes to it.
  This can be used to do things like:  
  * Set default argument values before the validations are run using `Ash.Changeset.set_arguments/2` or `Ash.Changeset.set_argument/3`
  * Set changeset context
  * Do any other pre-processing on the changeset

* `:prepare_params` - A 2-argument function that receives the params map and the :validate atom and should return prepared params.
  Called before the form is validated.

* `:transform_params` - A function for post-processing the form parameters before they are used for changeset validation/submission.
  Use a 3-argument function to pattern match on the `AshPhoenix.Form` struct.

* `:method` (`t:String.t/0`) - The http method to associate with the form. Defaults to `post` for creates, and `put` for everything else.

* `:exclude_fields_if_empty` - These fields will be ignored if they are empty strings.  
  This list of fields supports dead view forms. When a form is submitted from dead view
  empty fields are submitted as empty strings. This is problematic for fields that allow_nil
  or those that have default values.

* `:tenant` (`t:term/0`) - The current tenant. Passed through to the underlying action.

* `:params` (`t:term/0`) - The initial parameters to use for the form. This is useful for setting up a form with default values. The default value is `%{}`.

## Nested Form Options

`AshPhoenix.Form` automatically determines the nested forms available based on an action's usage of
`change manage_relationship(...)`. See the [Related Forms](/documentation/topics/nested-forms.md)
for more.

* `:type` - The cardinality of the nested form - `:list` or `:single`. Valid values are :list, :single The default value is `:single`.

* `:sparse?` (`t:boolean/0`) - If the nested form is `sparse`, the form won't expect all inputs for all forms to be present.  
  Has no effect if the type is `:single`.  
  Normally, if you leave some forms out of a list of nested forms, they are removed from the parameters
  passed to the action. For example, if you had a `post` with two comments `[%Comment{id: 1}, %Comment{id: 2}]`
  and you passed down params like `comments[0][id]=1&comments[1][text]=new_text`, we would remove the second comment
  from the input parameters, resulting in the following being passed into the action: `%{"comments" => [%{"id" => 1, "text" => "new"}]}`.
  By setting it to sparse, you have to explicitly use `remove_form` for that removal to happen. So in the same scenario above, the parameters
  that would be sent would actually be `%{"comments" => [%{"id" => 1, "text" => "new"}, %{"id" => 2}]}`.  
  One major difference with `sparse?` is that the form actually ignores the *index* provided, e.g `comments[0]...`, and instead uses the primary
  key e.g `comments[0][id]` to match which form is being updated. This prevents you from having to find the index of the specific item you want to
  update. Which could be very gnarly on deeply nested forms. If there is no primary key, or the primary key does not match anything, it is treated
  as a new form.  
  REMEMBER: You need to use `Phoenix.Components.inputs_for` to render the nested forms, or manually add hidden inputs using
  `hidden_inputs_for` (or `HiddenInputs` if using Surface) for the id to be automatically placed into the form.

* `:forms` (`t:keyword/0`) - Forms nested inside the current nesting level in all cases.

* `:for_type` - What action types the form applies for. Leave blank for it to apply to all action types. Valid values are :read, :create, :update

* `:merge?` (`t:boolean/0`) - When building parameters, this input will be merged with its parent input. This allows for combining multiple forms into a single input. The default value is `false`.

* `:for` (`t:atom/0`) - When creating parameters for the action, the key that the forms should be gathered into. Defaults to the key used to configure the nested form. Ignored if `merge?` is `true`.

* `:resource` (`t:atom/0`) - The resource of the nested forms. Unnecessary if you are providing the `data` key, and not adding additional forms to this path.

* `:create_action` (`t:atom/0`) - The create action to use when building new forms. Only necessary if you want to use `add_form/3` with this path.

* `:update_action` (`t:atom/0`) - The update action to use when building forms for data. Only necessary if you supply the `data` key.

* `:data` (`t:term/0`) - The current value or values that should have update forms built by default.  
  You can also provide a single argument function that will return the data based on the
  data of the parent form. This is important for multiple nesting levels of `:list` type
  forms, because the data depends on which parent is being rendered.

# `for_destroy`

```elixir
@spec for_destroy(Ash.Resource.record(), action :: atom(), opts :: Keyword.t()) :: t()
```

Creates a form corresponding to a destroy action on a record.

Options:
* `:actor` (`t:term/0`) - The actor performing the action. Passed through to the underlying action.

* `:scope` (`t:term/0`) - A value that implements the `Ash.Scope` protocol, for passing around actor/tenant/context in a single value. See `Ash.Scope` for more.

* `:forms` (`t:keyword/0`) - Nested form configurations. See `for_create/3` "Nested Form Options" docs for more.

* `:warn_on_unhandled_errors?` (`t:boolean/0`) - Warns on any errors that don't match the form pattern of `{:field, "message", [replacement: :vars]}` or implement the `AshPhoenix.FormData.Error` protocol. The default value is `true`.

* `:domain` (`t:atom/0`) - The domain to use when calling the action.

* `:as` (`t:String.t/0`) - The name of the form in the submitted params. You will need to pull the form params out using this key. The default value is `"form"`.

* `:id` (`t:String.t/0`) - The html id of the form. Defaults to the value of `:as` if provided, otherwise "form"

* `:transform_errors` - Allows for manual manipulation and transformation of errors. You may prefer `post_process_errors` as a simpler API.  
  If possible, try to implement `AshPhoenix.FormData.Error` for the error (if it as a custom one, for example).
  If that isn't possible, you can provide this function which will get the changeset and the error, and should
  return a list of ash phoenix formatted errors, e.g `[{field :: atom, message :: String.t(), substituations :: Keyword.t()}]`  
  Example:
  ```
  AshPhoenix.Form.for_create(..., transform_errors: fn
    _changeset, %{field: :field1} = error ->
      %{error | field: :field2}  
    _changeset, error ->
      error
  end
  ```

* `:post_process_errors` - Allows for post-processing of errors after they have been converted to the standard triple format.  
  This function receives the form, the path to the form, and an error triple `{field, message, vars}`.
  It should return either a modified triple or `nil` to filter out the error.  
  This is useful for:
  * Filtering out certain errors based on custom criteria
  * Remapping field names from one field to another
  * Modifying error messages or variables  
  Example:
  ```
  AshPhoenix.Form.for_create(..., post_process_errors: fn form, _path, {field, message, vars} ->
    case field do
      :status ->
        # hide these errors
        nil  
      field when field in [:currency, :amount] ->
        # remap the field, and replace the error message
        {:money, "please enter a real money amount", []}  
      field ->
        # leave the others unchanged
        {field, message, vars}
    end
  end)
  ```

* `:prepare_source` - A 1-argument function the receives the initial changeset (or query) and makes any relevant changes to it.
  This can be used to do things like:  
  * Set default argument values before the validations are run using `Ash.Changeset.set_arguments/2` or `Ash.Changeset.set_argument/3`
  * Set changeset context
  * Do any other pre-processing on the changeset

* `:prepare_params` - A 2-argument function that receives the params map and the :validate atom and should return prepared params.
  Called before the form is validated.

* `:transform_params` - A function for post-processing the form parameters before they are used for changeset validation/submission.
  Use a 3-argument function to pattern match on the `AshPhoenix.Form` struct.

* `:method` (`t:String.t/0`) - The http method to associate with the form. Defaults to `post` for creates, and `put` for everything else.

* `:exclude_fields_if_empty` - These fields will be ignored if they are empty strings.  
  This list of fields supports dead view forms. When a form is submitted from dead view
  empty fields are submitted as empty strings. This is problematic for fields that allow_nil
  or those that have default values.

* `:tenant` (`t:term/0`) - The current tenant. Passed through to the underlying action.

* `:params` (`t:term/0`) - The initial parameters to use for the form. This is useful for setting up a form with default values. The default value is `%{}`.

Any *additional* options will be passed to the underlying call to `Ash.Changeset.for_destroy/4`. This means
you can set things like the tenant/actor. These will be retained, and provided again when `Form.submit/3` is called.

## Nested Form Options

`AshPhoenix.Form` automatically determines the nested forms available based on an action's usage of
`change manage_relationship(...)`. See the [Related Forms](/documentation/topics/nested-forms.md)
for more.

* `:type` - The cardinality of the nested form - `:list` or `:single`. Valid values are :list, :single The default value is `:single`.

* `:sparse?` (`t:boolean/0`) - If the nested form is `sparse`, the form won't expect all inputs for all forms to be present.  
  Has no effect if the type is `:single`.  
  Normally, if you leave some forms out of a list of nested forms, they are removed from the parameters
  passed to the action. For example, if you had a `post` with two comments `[%Comment{id: 1}, %Comment{id: 2}]`
  and you passed down params like `comments[0][id]=1&comments[1][text]=new_text`, we would remove the second comment
  from the input parameters, resulting in the following being passed into the action: `%{"comments" => [%{"id" => 1, "text" => "new"}]}`.
  By setting it to sparse, you have to explicitly use `remove_form` for that removal to happen. So in the same scenario above, the parameters
  that would be sent would actually be `%{"comments" => [%{"id" => 1, "text" => "new"}, %{"id" => 2}]}`.  
  One major difference with `sparse?` is that the form actually ignores the *index* provided, e.g `comments[0]...`, and instead uses the primary
  key e.g `comments[0][id]` to match which form is being updated. This prevents you from having to find the index of the specific item you want to
  update. Which could be very gnarly on deeply nested forms. If there is no primary key, or the primary key does not match anything, it is treated
  as a new form.  
  REMEMBER: You need to use `Phoenix.Components.inputs_for` to render the nested forms, or manually add hidden inputs using
  `hidden_inputs_for` (or `HiddenInputs` if using Surface) for the id to be automatically placed into the form.

* `:forms` (`t:keyword/0`) - Forms nested inside the current nesting level in all cases.

* `:for_type` - What action types the form applies for. Leave blank for it to apply to all action types. Valid values are :read, :create, :update

* `:merge?` (`t:boolean/0`) - When building parameters, this input will be merged with its parent input. This allows for combining multiple forms into a single input. The default value is `false`.

* `:for` (`t:atom/0`) - When creating parameters for the action, the key that the forms should be gathered into. Defaults to the key used to configure the nested form. Ignored if `merge?` is `true`.

* `:resource` (`t:atom/0`) - The resource of the nested forms. Unnecessary if you are providing the `data` key, and not adding additional forms to this path.

* `:create_action` (`t:atom/0`) - The create action to use when building new forms. Only necessary if you want to use `add_form/3` with this path.

* `:update_action` (`t:atom/0`) - The update action to use when building forms for data. Only necessary if you supply the `data` key.

* `:data` (`t:term/0`) - The current value or values that should have update forms built by default.  
  You can also provide a single argument function that will return the data based on the
  data of the parent form. This is important for multiple nesting levels of `:list` type
  forms, because the data depends on which parent is being rendered.

# `for_read`

```elixir
@spec for_read(Ash.Resource.t(), action :: atom(), opts :: Keyword.t()) :: t()
```

Creates a form corresponding to a read action on a resource.

Options:
* `:actor` (`t:term/0`) - The actor performing the action. Passed through to the underlying action.

* `:scope` (`t:term/0`) - A value that implements the `Ash.Scope` protocol, for passing around actor/tenant/context in a single value. See `Ash.Scope` for more.

* `:forms` (`t:keyword/0`) - Nested form configurations. See `for_create/3` "Nested Form Options" docs for more.

* `:warn_on_unhandled_errors?` (`t:boolean/0`) - Warns on any errors that don't match the form pattern of `{:field, "message", [replacement: :vars]}` or implement the `AshPhoenix.FormData.Error` protocol. The default value is `true`.

* `:domain` (`t:atom/0`) - The domain to use when calling the action.

* `:as` (`t:String.t/0`) - The name of the form in the submitted params. You will need to pull the form params out using this key. The default value is `"form"`.

* `:id` (`t:String.t/0`) - The html id of the form. Defaults to the value of `:as` if provided, otherwise "form"

* `:transform_errors` - Allows for manual manipulation and transformation of errors. You may prefer `post_process_errors` as a simpler API.  
  If possible, try to implement `AshPhoenix.FormData.Error` for the error (if it as a custom one, for example).
  If that isn't possible, you can provide this function which will get the changeset and the error, and should
  return a list of ash phoenix formatted errors, e.g `[{field :: atom, message :: String.t(), substituations :: Keyword.t()}]`  
  Example:
  ```
  AshPhoenix.Form.for_create(..., transform_errors: fn
    _changeset, %{field: :field1} = error ->
      %{error | field: :field2}  
    _changeset, error ->
      error
  end
  ```

* `:post_process_errors` - Allows for post-processing of errors after they have been converted to the standard triple format.  
  This function receives the form, the path to the form, and an error triple `{field, message, vars}`.
  It should return either a modified triple or `nil` to filter out the error.  
  This is useful for:
  * Filtering out certain errors based on custom criteria
  * Remapping field names from one field to another
  * Modifying error messages or variables  
  Example:
  ```
  AshPhoenix.Form.for_create(..., post_process_errors: fn form, _path, {field, message, vars} ->
    case field do
      :status ->
        # hide these errors
        nil  
      field when field in [:currency, :amount] ->
        # remap the field, and replace the error message
        {:money, "please enter a real money amount", []}  
      field ->
        # leave the others unchanged
        {field, message, vars}
    end
  end)
  ```

* `:prepare_source` - A 1-argument function the receives the initial changeset (or query) and makes any relevant changes to it.
  This can be used to do things like:  
  * Set default argument values before the validations are run using `Ash.Changeset.set_arguments/2` or `Ash.Changeset.set_argument/3`
  * Set changeset context
  * Do any other pre-processing on the changeset

* `:prepare_params` - A 2-argument function that receives the params map and the :validate atom and should return prepared params.
  Called before the form is validated.

* `:transform_params` - A function for post-processing the form parameters before they are used for changeset validation/submission.
  Use a 3-argument function to pattern match on the `AshPhoenix.Form` struct.

* `:method` (`t:String.t/0`) - The http method to associate with the form. Defaults to `post` for creates, and `put` for everything else.

* `:exclude_fields_if_empty` - These fields will be ignored if they are empty strings.  
  This list of fields supports dead view forms. When a form is submitted from dead view
  empty fields are submitted as empty strings. This is problematic for fields that allow_nil
  or those that have default values.

* `:tenant` (`t:term/0`) - The current tenant. Passed through to the underlying action.

* `:params` (`t:term/0`) - The initial parameters to use for the form. This is useful for setting up a form with default values. The default value is `%{}`.

Any *additional* options will be passed to the underlying call to `Ash.Query.for_read/4`. This means
you can set things like the tenant/actor. These will be retained, and provided again when `Form.submit/3` is called.

Keep in mind that the `source` of the form in this case is a query, not a changeset. This means that, very likely,
you would not want to use nested forms here. However, it could make sense if you had a query argument that was an
embedded resource, so the capability remains.

## Nested Form Options

* `:type` - The cardinality of the nested form - `:list` or `:single`. Valid values are :list, :single The default value is `:single`.

* `:sparse?` (`t:boolean/0`) - If the nested form is `sparse`, the form won't expect all inputs for all forms to be present.  
  Has no effect if the type is `:single`.  
  Normally, if you leave some forms out of a list of nested forms, they are removed from the parameters
  passed to the action. For example, if you had a `post` with two comments `[%Comment{id: 1}, %Comment{id: 2}]`
  and you passed down params like `comments[0][id]=1&comments[1][text]=new_text`, we would remove the second comment
  from the input parameters, resulting in the following being passed into the action: `%{"comments" => [%{"id" => 1, "text" => "new"}]}`.
  By setting it to sparse, you have to explicitly use `remove_form` for that removal to happen. So in the same scenario above, the parameters
  that would be sent would actually be `%{"comments" => [%{"id" => 1, "text" => "new"}, %{"id" => 2}]}`.  
  One major difference with `sparse?` is that the form actually ignores the *index* provided, e.g `comments[0]...`, and instead uses the primary
  key e.g `comments[0][id]` to match which form is being updated. This prevents you from having to find the index of the specific item you want to
  update. Which could be very gnarly on deeply nested forms. If there is no primary key, or the primary key does not match anything, it is treated
  as a new form.  
  REMEMBER: You need to use `Phoenix.Components.inputs_for` to render the nested forms, or manually add hidden inputs using
  `hidden_inputs_for` (or `HiddenInputs` if using Surface) for the id to be automatically placed into the form.

* `:forms` (`t:keyword/0`) - Forms nested inside the current nesting level in all cases.

* `:for_type` - What action types the form applies for. Leave blank for it to apply to all action types. Valid values are :read, :create, :update

* `:merge?` (`t:boolean/0`) - When building parameters, this input will be merged with its parent input. This allows for combining multiple forms into a single input. The default value is `false`.

* `:for` (`t:atom/0`) - When creating parameters for the action, the key that the forms should be gathered into. Defaults to the key used to configure the nested form. Ignored if `merge?` is `true`.

* `:resource` (`t:atom/0`) - The resource of the nested forms. Unnecessary if you are providing the `data` key, and not adding additional forms to this path.

* `:create_action` (`t:atom/0`) - The create action to use when building new forms. Only necessary if you want to use `add_form/3` with this path.

* `:update_action` (`t:atom/0`) - The update action to use when building forms for data. Only necessary if you supply the `data` key.

* `:data` (`t:term/0`) - The current value or values that should have update forms built by default.  
  You can also provide a single argument function that will return the data based on the
  data of the parent form. This is important for multiple nesting levels of `:list` type
  forms, because the data depends on which parent is being rendered.

# `for_update`

```elixir
@spec for_update(Ash.Resource.record(), action :: atom(), opts :: Keyword.t()) :: t()
```

Creates a form corresponding to an update action on a record.

Options:
* `:actor` (`t:term/0`) - The actor performing the action. Passed through to the underlying action.

* `:scope` (`t:term/0`) - A value that implements the `Ash.Scope` protocol, for passing around actor/tenant/context in a single value. See `Ash.Scope` for more.

* `:forms` (`t:keyword/0`) - Nested form configurations. See `for_create/3` "Nested Form Options" docs for more.

* `:warn_on_unhandled_errors?` (`t:boolean/0`) - Warns on any errors that don't match the form pattern of `{:field, "message", [replacement: :vars]}` or implement the `AshPhoenix.FormData.Error` protocol. The default value is `true`.

* `:domain` (`t:atom/0`) - The domain to use when calling the action.

* `:as` (`t:String.t/0`) - The name of the form in the submitted params. You will need to pull the form params out using this key. The default value is `"form"`.

* `:id` (`t:String.t/0`) - The html id of the form. Defaults to the value of `:as` if provided, otherwise "form"

* `:transform_errors` - Allows for manual manipulation and transformation of errors. You may prefer `post_process_errors` as a simpler API.  
  If possible, try to implement `AshPhoenix.FormData.Error` for the error (if it as a custom one, for example).
  If that isn't possible, you can provide this function which will get the changeset and the error, and should
  return a list of ash phoenix formatted errors, e.g `[{field :: atom, message :: String.t(), substituations :: Keyword.t()}]`  
  Example:
  ```
  AshPhoenix.Form.for_create(..., transform_errors: fn
    _changeset, %{field: :field1} = error ->
      %{error | field: :field2}  
    _changeset, error ->
      error
  end
  ```

* `:post_process_errors` - Allows for post-processing of errors after they have been converted to the standard triple format.  
  This function receives the form, the path to the form, and an error triple `{field, message, vars}`.
  It should return either a modified triple or `nil` to filter out the error.  
  This is useful for:
  * Filtering out certain errors based on custom criteria
  * Remapping field names from one field to another
  * Modifying error messages or variables  
  Example:
  ```
  AshPhoenix.Form.for_create(..., post_process_errors: fn form, _path, {field, message, vars} ->
    case field do
      :status ->
        # hide these errors
        nil  
      field when field in [:currency, :amount] ->
        # remap the field, and replace the error message
        {:money, "please enter a real money amount", []}  
      field ->
        # leave the others unchanged
        {field, message, vars}
    end
  end)
  ```

* `:prepare_source` - A 1-argument function the receives the initial changeset (or query) and makes any relevant changes to it.
  This can be used to do things like:  
  * Set default argument values before the validations are run using `Ash.Changeset.set_arguments/2` or `Ash.Changeset.set_argument/3`
  * Set changeset context
  * Do any other pre-processing on the changeset

* `:prepare_params` - A 2-argument function that receives the params map and the :validate atom and should return prepared params.
  Called before the form is validated.

* `:transform_params` - A function for post-processing the form parameters before they are used for changeset validation/submission.
  Use a 3-argument function to pattern match on the `AshPhoenix.Form` struct.

* `:method` (`t:String.t/0`) - The http method to associate with the form. Defaults to `post` for creates, and `put` for everything else.

* `:exclude_fields_if_empty` - These fields will be ignored if they are empty strings.  
  This list of fields supports dead view forms. When a form is submitted from dead view
  empty fields are submitted as empty strings. This is problematic for fields that allow_nil
  or those that have default values.

* `:tenant` (`t:term/0`) - The current tenant. Passed through to the underlying action.

* `:params` (`t:term/0`) - The initial parameters to use for the form. This is useful for setting up a form with default values. The default value is `%{}`.

Any *additional* options will be passed to the underlying call to `Ash.Changeset.for_update/4`. This means
you can set things like the tenant/actor. These will be retained, and provided again when `Form.submit/3` is called.

## Nested Form Options

`AshPhoenix.Form` automatically determines the nested forms available based on an action's usage of
`change manage_relationship(...)`. See the [Related Forms](/documentation/topics/nested-forms.md)
for more.

* `:type` - The cardinality of the nested form - `:list` or `:single`. Valid values are :list, :single The default value is `:single`.

* `:sparse?` (`t:boolean/0`) - If the nested form is `sparse`, the form won't expect all inputs for all forms to be present.  
  Has no effect if the type is `:single`.  
  Normally, if you leave some forms out of a list of nested forms, they are removed from the parameters
  passed to the action. For example, if you had a `post` with two comments `[%Comment{id: 1}, %Comment{id: 2}]`
  and you passed down params like `comments[0][id]=1&comments[1][text]=new_text`, we would remove the second comment
  from the input parameters, resulting in the following being passed into the action: `%{"comments" => [%{"id" => 1, "text" => "new"}]}`.
  By setting it to sparse, you have to explicitly use `remove_form` for that removal to happen. So in the same scenario above, the parameters
  that would be sent would actually be `%{"comments" => [%{"id" => 1, "text" => "new"}, %{"id" => 2}]}`.  
  One major difference with `sparse?` is that the form actually ignores the *index* provided, e.g `comments[0]...`, and instead uses the primary
  key e.g `comments[0][id]` to match which form is being updated. This prevents you from having to find the index of the specific item you want to
  update. Which could be very gnarly on deeply nested forms. If there is no primary key, or the primary key does not match anything, it is treated
  as a new form.  
  REMEMBER: You need to use `Phoenix.Components.inputs_for` to render the nested forms, or manually add hidden inputs using
  `hidden_inputs_for` (or `HiddenInputs` if using Surface) for the id to be automatically placed into the form.

* `:forms` (`t:keyword/0`) - Forms nested inside the current nesting level in all cases.

* `:for_type` - What action types the form applies for. Leave blank for it to apply to all action types. Valid values are :read, :create, :update

* `:merge?` (`t:boolean/0`) - When building parameters, this input will be merged with its parent input. This allows for combining multiple forms into a single input. The default value is `false`.

* `:for` (`t:atom/0`) - When creating parameters for the action, the key that the forms should be gathered into. Defaults to the key used to configure the nested form. Ignored if `merge?` is `true`.

* `:resource` (`t:atom/0`) - The resource of the nested forms. Unnecessary if you are providing the `data` key, and not adding additional forms to this path.

* `:create_action` (`t:atom/0`) - The create action to use when building new forms. Only necessary if you want to use `add_form/3` with this path.

* `:update_action` (`t:atom/0`) - The update action to use when building forms for data. Only necessary if you supply the `data` key.

* `:data` (`t:term/0`) - The current value or values that should have update forms built by default.  
  You can also provide a single argument function that will return the data based on the
  data of the parent form. This is important for multiple nesting levels of `:list` type
  forms, because the data depends on which parent is being rendered.

# `get_form`

```elixir
@spec get_form(t() | Phoenix.HTML.Form.t(), path()) :: t() | nil
```

Gets the form at the specified path

# `has_form?`

```elixir
@spec has_form?(t(), path()) :: boolean()
```

Returns true if a given form path exists in the form

# `hidden_fields`

```elixir
@spec hidden_fields(t() | Phoenix.HTML.Form.t()) :: Keyword.t()
```

Returns the hidden fields for a form as a keyword list

# `ignore`

```elixir
@spec ignore(t()) :: t()
```

Toggles the form to be ignored or not ignored.

To set this manually in an html form, use the field `:_ignored` and set it
to the string "true". Any other value will not result in the form being ignored.

# `ignored?`

```elixir
@spec ignored?(t() | Phoenix.HTML.Form.t()) :: boolean()
```

Returns true if the form is ignored

# `merge_options`

```elixir
@spec merge_options(t(), Keyword.t()) :: t()
@spec merge_options(Phoenix.HTML.Form.t(), Keyword.t()) :: Phoenix.HTML.Form.t()
```

Merge the new options with the saved options on a form. See `update_options/2` for more.

# `params`

Returns the parameters from the form that would be submitted to the action.

This can be useful if you want to get the parameters and manipulate them/build a custom changeset
afterwards.

# `parse_path!`

```elixir
@spec parse_path!(t() | Phoenix.HTML.Form.t(), path(), opts :: Keyword.t()) ::
  [atom() | integer()] | no_return()
```

A utility for parsing paths of nested forms in query encoded format.

For example:

```elixir
parse_path!(form, "post[comments][0][sub_comments][0])

[:comments, 0, :sub_comments, 0]
```

# `raw_errors`

Returns the raw errors from the underlying source without protocol formatting.

This function provides access to the original errors from the changeset, query, or
action input without any transformation through the AshPhoenix.FormData.Error protocol.

## Options

* `:for_path` - The path to get errors for. Defaults to `[]` (the current form).
  Set to `:all` to get all errors for all forms.
* `:format` - This option is ignored for `raw_errors/2` as it always returns raw errors.

## Examples

    raw_errors(form)
    #=> [%Ash.Error.Invalid{...}, ...]

    raw_errors(form, for_path: [:posts, 0])
    #=> [%Ash.Error.Invalid{...}, ...]

    raw_errors(form, for_path: :all)
    #=> %{[] => [...], [:posts, 0] => [...]}

# `remove_form`

```elixir
@spec remove_form(t(), path(), Keyword.t()) :: t()
@spec remove_form(Phoenix.HTML.Form.t(), path(), Keyword.t()) :: Phoenix.HTML.Form.t()
```

Removes a form at the provided path.

See `add_form/3` for more information on the `path` argument.

If you are not using liveview, and you want to support removing forms that were created based on the `data`
option from the browser, you'll need to include in the form submission a custom list of strings to remove, and
then manually iterate over them in your controller, for example:

```elixir
Enum.reduce(removed_form_paths, form, &AshPhoenix.Form.remove_form(&2, &1))
```

* `:validate?` (`t:boolean/0`) - Validates the new full form. The default value is `true`.

* `:validate_opts` (`t:term/0`) - Options to pass to `validate`. Only used if `validate?` is set to `true` (the default) The default value is `[]`.

# `set_data`

Sets the data of the form, in addition to the data of the underlying source, if applicable.

Queries do not track data (because that wouldn't make sense), so this will not update the data
for read actions

# `sort_forms`

```elixir
@spec sort_forms(
  t(),
  [atom() | integer()],
  [String.t() | integer()] | :increment | :decrement
) :: t()
```

This function sorts nested forms at the specified path.

The path can either be an attribute, e.g. [:comments] (for changing the entire order) or an attribute with an index,
like `[:comments, 2]` (used for incrementing or decrementing).

You can provide the following instructions:
- `[0, 1, 2]`: indices in the new order (they can be either strings or integers).
- `:increment`: increment the index of a specific form, swapping it with the next form.
- `:decrement`: decrement the index of a specific form, swapping it with the previous form.

# `submit`

```elixir
@spec submit(t(), Keyword.t()) ::
  {:ok, Ash.Resource.record() | nil | [Ash.Notifier.Notification.t()]}
  | {:ok, Ash.Resource.record(), [Ash.Notifier.Notification.t()]}
  | :ok
  | {:error, t()}
@spec submit(Phoenix.HTML.Form.t(), Keyword.t()) ::
  {:ok, Ash.Resource.record() | nil | [Ash.Notifier.Notification.t()]}
  | {:ok, Ash.Resource.record(), [Ash.Notifier.Notification.t()]}
  | :ok
  | {:error, Phoenix.HTML.Form.t()}
```

Submits the form.

If the submission returns an error, the resulting form can be rerendered. Any nested
errors will be passed down to the corresponding form for that input.

Options:

* `:force?` (`t:boolean/0`) - Submit the form even if it is invalid in its current state. The default value is `false`.

* `:action_opts` (`t:keyword/0`) - Opts to pass to the call to Ash when calling the action. The default value is `[]`.

* `:errors` (`t:boolean/0`) - Wether or not to show errors after submitting. The default value is `true`.

* `:override_params` (`t:term/0`) - If specified, then the params are not extracted from the form.  
  How this different from `params`: providing `params` is simply results in calling `validate(form, params)` before proceeding.
  The values that are passed into the action are then extracted from the form using `params/2`. With `override_params`, the form
  is not validated again, and the `override_params` are passed directly into the action.

* `:params` (`t:term/0`) - If specified, `validate/3` is called with the new params before submitting the form.  
  This is a shortcut to avoid needing to explicitly validate before every submit.  
  For example:  
  ```elixir
  form
  |> AshPhoenix.Form.validate(params)
  |> AshPhoenix.Form.submit()
  ```  
  Is the same as:  
  ```elixir
  form
  |> AshPhoenix.Form.submit(params: params)
  ```

* `:read_one?` (`t:boolean/0`) - If submitting a read form, a single result will be returned (via read_one) instead of a list of results.  
  Ignored for non-read forms. The default value is `false`.

* `:before_submit` (function of arity 1) - A function to apply to the source (changeset or query) just before submitting the action. Must return the modified changeset.

# `submit!`

```elixir
@spec submit!(t(), Keyword.t()) :: Ash.Resource.record() | :ok | no_return()
```

Same as `submit/2`, but raises an error if the submission fails.

# `touch`

Mark a field or fields as touched

To mark nested fields as touched use with `update_form/4` or `update_forms_at_path/4`

# `update_form`

Updates the form at the provided path using the given function.

Marks all forms along the path as touched by default. To prevent it, provide `mark_as_touched?: false`.

This can be useful if you have a button that should modify a nested form in some way, for example.

# `update_forms_at_path`

Updates the list of forms matching a given path. Does not validate that the path points at a single form like `update_form/4`.

Additionally, if it gets to a list of child forms and the next part of the path is not an integer,
it will update all of the forms at that path.

# `update_options`

Update the saved options on a form.

When a form is created, options like `actor` and `authorize?` are stored in the `opts` key.
If you have a case where these options change over time, for example a select box that determines the actor, use this function to override those opts.

You may want to validate again after this has been changed if it can change the results of your form validation.

# `update_params`

```elixir
@spec update_params(t(), fun :: (map() -&gt; map()), validate_opts :: Keyword.t()) :: t()
@spec update_params(
  Phoenix.HTML.Form.t(),
  params :: (map() -&gt; map()),
  validate_opts :: Keyword.t()
) ::
  Phoenix.HTML.Form.t()
```

Update the previous params provided to the form, and revalidate.

Accepts the same options as `validate/2`, passing them through directly.

You should prefer to use `validate/2` when you have all of the params from the form.
This is meant for cases when some event has occured that should modify the params,
not as a replacement for `validate/2`.

This can be useful for things like customized inputs or buttons, that have special
handlers in your live view. For example, if you have an appointment that expresses
a list of available times in the UI, but the action just takes a single `time` argument,
you can make each available time a button, like so:

```heex
<.button phx-click="time-selected" phx-value-time="<%= time %>" />
```

and then have an event handler like this:

```elixir
def handle_event("time-selected", %{"time" => time}, socket) do
  form = AshPhoenix.Form.update_params(socket.assigns.form, &Map.put(&1, "time", time))
  {:noreply, assign(socket, :form, form)}
end
```

# `validate`

```elixir
@spec validate(t(), map(), Keyword.t()) :: t()
@spec validate(Phoenix.HTML.Form.t(), map(), Keyword.t()) :: Phoenix.HTML.Form.t()
```

Validates the parameters against the form.

Options:

* `:errors` (`t:boolean/0`) - Set to false to hide errors after validation. The default value is `true`.

* `:target` (list of `t:String.t/0`) - The `_target` param provided by phoenix. Used to support the `only_touched?` option.

* `:only_touched?` (`t:boolean/0`) - If set to true, only fields that have been marked as touched will be used  
  If you use this for validation you likely want to use it when submitting as well. The default value is `false`.

# `value`

```elixir
@spec value(t() | Phoenix.HTML.Form.t(), atom()) :: any()
```

Gets the value for a given field in the form.

---

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