#
tokens: 62028/50000 1/34 files (page 7/10)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 7 of 10. Use http://codebase.md/m-gonzalo/cosa-sai?lines=true&page={x} to view the full context.

# Directory Structure

```
├── .gitignore
├── bun.lock
├── Dockerfile
├── package.json
├── prompts
│   └── default.txt
├── README.md
├── smithery.yaml
├── src
│   ├── db.ts
│   ├── gemini.ts
│   ├── index.ts
│   ├── logger.ts
│   └── types.ts
├── test
│   ├── docs
│   │   ├── ash-docs
│   │   │   ├── ash_admin.md
│   │   │   ├── ash_appsignal.md
│   │   │   ├── ash_archival.md
│   │   │   ├── ash_authentication_phoenix.md
│   │   │   ├── ash_authentication.md
│   │   │   ├── ash_cloak.md
│   │   │   ├── ash_csv.md
│   │   │   ├── ash_cubdb.md
│   │   │   ├── ash_double_entry.md
│   │   │   ├── ash_graphql.md
│   │   │   ├── ash_json_api.md
│   │   │   ├── ash_money.md
│   │   │   ├── ash_oban.md
│   │   │   ├── ash_phoenix.md
│   │   │   ├── ash_postgres.md
│   │   │   ├── ash_rbac.md
│   │   │   ├── ash_sqlite.md
│   │   │   ├── ash_state_machine.md
│   │   │   └── ash.md
│   │   ├── bun-elysia-docs
│   │   │   ├── bun.sh.md
│   │   │   └── elysiajs.com.md
│   │   ├── javascript-docs
│   │   │   └── sample.md
│   │   └── phoenix-docs
│   │       └── phx-docs.md
│   └── prompts
│       ├── ash-framework.txt
│       ├── bun-elysia.txt
│       ├── javascript.txt
│       └── phoenix.txt
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/test/docs/ash-docs/ash_phoenix.md:
--------------------------------------------------------------------------------

```markdown
   1 | [![ash_phoenix](assets/logo.png)](https://github.com/ash-project/ash_phoenix)
   2 | 
   3 | [ash\_phoenix](https://github.com/ash-project/ash_phoenix)
   4 | 
   5 | v2.1.14
   6 | 
   7 | - Pages
   8 | - Modules
   9 | - Mix Tasks
  10 | 
  11 | <!--THE END-->
  12 | 
  13 | <!--THE END-->
  14 | 
  15 | <!--THE END-->
  16 | 
  17 | Search documentation of ash\_phoenix
  18 | 
  19 | Settings
  20 | 
  21 | # [View Source](https://github.com/ash-project/ash_phoenix "View Source") API Reference ash\_phoenix v2.1.14
  22 | 
  23 | ## [](api-reference.html#modules)Modules
  24 | 
  25 | [AshPhoenix](AshPhoenix.html)
  26 | 
  27 | An extension to add form builders to the code interface.
  28 | 
  29 | [AshPhoenix.FilterForm](AshPhoenix.FilterForm.html)
  30 | 
  31 | A module to help you create complex forms that generate Ash filters.
  32 | 
  33 | [AshPhoenix.FilterForm.Arguments](AshPhoenix.FilterForm.Arguments.html)
  34 | 
  35 | Represents the arguments to a calculation being filtered on
  36 | 
  37 | [AshPhoenix.FilterForm.Predicate](AshPhoenix.FilterForm.Predicate.html)
  38 | 
  39 | Represents an individual predicate appearing in a filter form.
  40 | 
  41 | [AshPhoenix.Form](AshPhoenix.Form.html)
  42 | 
  43 | A module to allow you to fluidly use resources with Phoenix forms.
  44 | 
  45 | [AshPhoenix.Form.Auto](AshPhoenix.Form.Auto.html)
  46 | 
  47 | A tool to automatically generate available nested forms based on a resource and action.
  48 | 
  49 | [AshPhoenix.Form.InvalidPath](AshPhoenix.Form.InvalidPath.html)
  50 | 
  51 | Raised when an invalid path is used to find, update or remove a form
  52 | 
  53 | [AshPhoenix.Form.NoActionConfigured](AshPhoenix.Form.NoActionConfigured.html)
  54 | 
  55 | Raised when a form action should happen but no action of the appropriate type has been configured
  56 | 
  57 | [AshPhoenix.Form.NoDataLoaded](AshPhoenix.Form.NoDataLoaded.html)
  58 | 
  59 | Raised when a data needed to be used but the required data was not loaded
  60 | 
  61 | [AshPhoenix.Form.NoFormConfigured](AshPhoenix.Form.NoFormConfigured.html)
  62 | 
  63 | Raised when attempting to refer to a form but no nested form with that name was configured.
  64 | 
  65 | [AshPhoenix.Form.NoResourceConfigured](AshPhoenix.Form.NoResourceConfigured.html)
  66 | 
  67 | Raised when a form needed to be constructed but the resource for that form could not be determined
  68 | 
  69 | [AshPhoenix.Form.WrappedValue](AshPhoenix.Form.WrappedValue.html)
  70 | 
  71 | A sentinal value used when editing a union that has non-map values
  72 | 
  73 | [AshPhoenix.FormData.Error](AshPhoenix.FormData.Error.html)
  74 | 
  75 | A protocol for allowing errors to be rendered into a form.
  76 | 
  77 | [AshPhoenix.LiveView](AshPhoenix.LiveView.html)
  78 | 
  79 | Utilities for keeping Ash query results up to date in a LiveView.
  80 | 
  81 | [AshPhoenix.SubdomainPlug](AshPhoenix.SubdomainPlug.html)
  82 | 
  83 | This is a basic plug that loads the current tenant assign from a given value set on subdomain.
  84 | 
  85 | ## [](api-reference.html#mix-tasks)Mix Tasks
  86 | 
  87 | [mix ash\_phoenix.gen.html](Mix.Tasks.AshPhoenix.Gen.Html.html)
  88 | 
  89 | This task renders .ex and .heex templates and copies them to specified directories.
  90 | 
  91 | [mix ash\_phoenix.gen.live](Mix.Tasks.AshPhoenix.Gen.Live.html)
  92 | 
  93 | Generates liveviews for a given domain and resource.
  94 | 
  95 | [Next Page → Home](readme.html)
  96 | 
  97 | [Hex Package](https://hex.pm/packages/ash_phoenix/2.1.14) [Hex Preview](https://preview.hex.pm/preview/ash_phoenix/2.1.14) Search HexDocs [Download ePub version](ash_phoenix.epub "ePub version")
  98 | 
  99 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.36.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
 100 | [![ash_phoenix](assets/logo.png)](https://github.com/ash-project/ash_phoenix)
 101 | 
 102 | [ash\_phoenix](https://github.com/ash-project/ash_phoenix)
 103 | 
 104 | v2.1.14
 105 | 
 106 | - Pages
 107 | - Modules
 108 | - Mix Tasks
 109 | 
 110 | <!--THE END-->
 111 | 
 112 | <!--THE END-->
 113 | 
 114 | <!--THE END-->
 115 | 
 116 | Search documentation of ash\_phoenix
 117 | 
 118 | Settings
 119 | 
 120 | # [View Source](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/filter_form/arguments.ex#L1 "View Source") AshPhoenix.FilterForm.Arguments (ash\_phoenix v2.1.14)
 121 | 
 122 | Represents the arguments to a calculation being filtered on
 123 | 
 124 | # [](AshPhoenix.FilterForm.Arguments.html#summary)Summary
 125 | 
 126 | ## [Functions](AshPhoenix.FilterForm.Arguments.html#functions)
 127 | 
 128 | [errors(arguments, transform\_errors)](AshPhoenix.FilterForm.Arguments.html#errors/2)
 129 | 
 130 | [new(params, arguments)](AshPhoenix.FilterForm.Arguments.html#new/2)
 131 | 
 132 | [validate\_arguments(arguments, params)](AshPhoenix.FilterForm.Arguments.html#validate_arguments/2)
 133 | 
 134 | # [](AshPhoenix.FilterForm.Arguments.html#functions)Functions
 135 | 
 136 | [](AshPhoenix.FilterForm.Arguments.html#errors/2)
 137 | 
 138 | # errors(arguments, transform\_errors)
 139 | 
 140 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/filter_form/arguments.ex#L56)
 141 | 
 142 | [](AshPhoenix.FilterForm.Arguments.html#new/2)
 143 | 
 144 | # new(params, arguments)
 145 | 
 146 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/filter_form/arguments.ex#L6)
 147 | 
 148 | [](AshPhoenix.FilterForm.Arguments.html#validate_arguments/2)
 149 | 
 150 | # validate\_arguments(arguments, params)
 151 | 
 152 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/filter_form/arguments.ex#L15)
 153 | 
 154 | [Hex Package](https://hex.pm/packages/ash_phoenix/2.1.14) [Hex Preview](https://preview.hex.pm/preview/ash_phoenix/2.1.14) Search HexDocs [Download ePub version](ash_phoenix.epub "ePub version")
 155 | 
 156 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.36.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
 157 | [![ash_phoenix](assets/logo.png)](https://github.com/ash-project/ash_phoenix)
 158 | 
 159 | [ash\_phoenix](https://github.com/ash-project/ash_phoenix)
 160 | 
 161 | v2.1.14
 162 | 
 163 | - Pages
 164 | - Modules
 165 | - Mix Tasks
 166 | 
 167 | <!--THE END-->
 168 | 
 169 | <!--THE END-->
 170 | 
 171 | <!--THE END-->
 172 | 
 173 | Search documentation of ash\_phoenix
 174 | 
 175 | Settings
 176 | 
 177 | # [View Source](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/filter_form/filter_form.ex#L1 "View Source") AshPhoenix.FilterForm (ash\_phoenix v2.1.14)
 178 | 
 179 | A module to help you create complex forms that generate Ash filters.
 180 | 
 181 | ```
 182 | # Create a FilterForm
 183 | filter_form = AshPhoenix.FilterForm.new(MyApp.Payroll.Employee)
 184 | ```
 185 | 
 186 | FilterForm's comprise 2 concepts, predicates and groups. Predicates are the simple boolean expressions you can use to build a query (`name == "Joe"`), and groups can be used to group predicates and more groups together. Groups can apply `and` or `or` operators to its nested components.
 187 | 
 188 | ```
 189 | # Add a predicate to the root of the form (which is itself a group)
 190 | filter_form = AshPhoenix.add_predicate(filter_form, :some_field, :eq, "Some Value")
 191 | 
 192 | # Add a group and another predicate to that group
 193 | {filter_form, group_id} = AshPhoenix.add_group(filter_form, operator: :or, return_id?: true)
 194 | filter_form = AshPhoenix.add_predicate(filter_form, :another, :eq, "Other", to: group_id)
 195 | ```
 196 | 
 197 | [`validate/1`](AshPhoenix.FilterForm.html#validate/1) is used to merge the submitted form params into the filter form, and one of the provided filter functions to apply the filter as a query, or generate an expression map, depending on your requirements:
 198 | 
 199 | ```
 200 | filter_form = AshPhoenix.validate(socket.assigns.filter_form, params)
 201 | 
 202 | # Generate a query and pass it to the Domain
 203 | query = AshPhoenix.FilterForm.filter!(MyApp.Payroll.Employee, filter_form)
 204 | filtered_employees = MyApp.Payroll.read!(query)
 205 | 
 206 | # Or use one of the other filter functions
 207 | AshPhoenix.FilterForm.to_filter_expression(filter_form)
 208 | AshPhoenix.FilterForm.to_filter_map(filter_form)
 209 | ```
 210 | 
 211 | ## [](AshPhoenix.FilterForm.html#module-liveview-example)LiveView Example
 212 | 
 213 | You can build a form and handle adding and removing nested groups and predicates with the following:
 214 | 
 215 | ```
 216 | alias MyApp.Payroll.Employee
 217 | 
 218 | @impl true
 219 | def render(assigns) do
 220 |   ~H"""
 221 |   <.simple_form
 222 |     :let={filter_form}
 223 |     for={@filter_form}
 224 |     phx-change="filter_validate"
 225 |     phx-submit="filter_submit"
 226 |   >
 227 |     <.filter_form_component component={filter_form} />
 228 |     <:actions>
 229 |       <.button>Submit</.button>
 230 |     </:actions>
 231 |   </.simple_form>
 232 |   <.table id="employees" rows={@employees}>
 233 |     <:col :let={employee} label="Payroll ID"><%= employee.employee_id %></:col>
 234 |     <:col :let={employee} label="Name"><%= employee.name %></:col>
 235 |     <:col :let={employee} label="Position"><%= employee.position %></:col>
 236 |   </.table>
 237 |   """
 238 | end
 239 | 
 240 | attr :component, :map, required: true, doc: "Could be a FilterForm (group) or a Predicate"
 241 | 
 242 | defp filter_form_component(%{component: %{source: %AshPhoenix.FilterForm{}}} = assigns) do
 243 |   ~H"""
 244 |   <div class="border-gray-50 border-8 p-4 rounded-xl mt-4">
 245 |     <div class="flex flex-row justify-between">
 246 |       <div class="flex flex-row gap-2 items-center">Filter</div>
 247 |       <div class="flex flex-row gap-2 items-center">
 248 |         <.input type="select" field={@component[:operator]} options={["and", "or"]} />
 249 |         <.button phx-click="add_filter_group" phx-value-component-id={@component.source.id} type="button">
 250 |           Add Group
 251 |         </.button>
 252 |         <.button
 253 |           phx-click="add_filter_predicate"
 254 |           phx-value-component-id={@component.source.id}
 255 |           type="button"
 256 |         >
 257 |           Add Predicate
 258 |         </.button>
 259 |         <.button
 260 |           phx-click="remove_filter_component"
 261 |           phx-value-component-id={@component.source.id}
 262 |           type="button"
 263 |         >
 264 |           Remove Group
 265 |         </.button>
 266 |       </div>
 267 |     </div>
 268 |     <.inputs_for :let={component} field={@component[:components]}>
 269 |       <.filter_form_component component={component} />
 270 |     </.inputs_for>
 271 |   </div>
 272 |   """
 273 | end
 274 | 
 275 | defp filter_form_component(
 276 |        %{component: %{source: %AshPhoenix.FilterForm.Predicate{}}} = assigns
 277 |      ) do
 278 |   ~H"""
 279 |   <div class="flex flex-row gap-2 mt-4">
 280 |     <.input
 281 |       type="select"
 282 |       options={AshPhoenix.FilterForm.fields(Employee)}
 283 |       field={@component[:field]}
 284 |     />
 285 |     <.input
 286 |       type="select"
 287 |       options={AshPhoenix.FilterForm.predicates(Employee)}
 288 |       field={@component[:operator]}
 289 |     />
 290 |     <.input field={@component[:value]} />
 291 |     <.button
 292 |       phx-click="remove_filter_component"
 293 |       phx-value-component-id={@component.source.id}
 294 |       type="button"
 295 |     >
 296 |       Remove
 297 |     </.button>
 298 |   </div>
 299 |   """
 300 | end
 301 | 
 302 | @impl true
 303 | def mount(_params, _session, socket) do
 304 |   socket =
 305 |     socket
 306 |     |> assign(:filter_form, AshPhoenix.FilterForm.new(Employee))
 307 |     |> assign(:employees, Employee.read_all!())
 308 | 
 309 |   {:ok, socket}
 310 | end
 311 | 
 312 | @impl true
 313 | def handle_event("filter_validate", %{"filter" => params}, socket) do
 314 |   {:noreply,
 315 |    assign(socket,
 316 |      filter_form: AshPhoenix.FilterForm.validate(socket.assigns.filter_form, params)
 317 |    )}
 318 | end
 319 | 
 320 | def handle_event("filter_submit", %{"filter" => params}, socket) do
 321 |   filter_form = AshPhoenix.FilterForm.validate(socket.assigns.filter_form, params)
 322 | 
 323 |   case AshPhoenix.FilterForm.filter(Employee, filter_form) do
 324 |     {:ok, query} ->
 325 |       {:noreply,
 326 |        socket
 327 |        |> assign(:employees, Employee.read_all!(query: query))
 328 |        |> assign(:filter_form, filter_form)}
 329 | 
 330 |     {:error, filter_form} ->
 331 |       {:noreply, assign(socket, filter_form: filter_form)}
 332 |   end
 333 | end
 334 | 
 335 | def handle_event("remove_filter_component", %{"component-id" => component_id}, socket) do
 336 |   {:noreply,
 337 |    assign(socket,
 338 |      filter_form:
 339 |        AshPhoenix.FilterForm.remove_component(socket.assigns.filter_form, component_id)
 340 |    )}
 341 | end
 342 | 
 343 | def handle_event("add_filter_group", %{"component-id" => component_id}, socket) do
 344 |   {:noreply,
 345 |    assign(socket,
 346 |      filter_form: AshPhoenix.FilterForm.add_group(socket.assigns.filter_form, to: component_id)
 347 |    )}
 348 | end
 349 | 
 350 | def handle_event("add_filter_predicate", %{"component-id" => component_id}, socket) do
 351 |   {:noreply,
 352 |    assign(socket,
 353 |      filter_form:
 354 |        AshPhoenix.FilterForm.add_predicate(socket.assigns.filter_form, :name, :contains, nil,
 355 |          to: component_id
 356 |        )
 357 |    )}
 358 | end
 359 | ```
 360 | 
 361 | # [](AshPhoenix.FilterForm.html#summary)Summary
 362 | 
 363 | ## [Functions](AshPhoenix.FilterForm.html#functions)
 364 | 
 365 | [add\_group(form, opts \\\\ \[\])](AshPhoenix.FilterForm.html#add_group/2)
 366 | 
 367 | Add a group to the filter. A group can contain predicates and other groups, allowing you to build quite complex nested filters.
 368 | 
 369 | [add\_predicate(form, field, operator\_or\_function, value, opts \\\\ \[\])](AshPhoenix.FilterForm.html#add_predicate/5)
 370 | 
 371 | Add a predicate to the filter.
 372 | 
 373 | [errors(form, opts \\\\ \[\])](AshPhoenix.FilterForm.html#errors/2)
 374 | 
 375 | Returns a flat list of all errors on all predicates in the filter, made safe for display in a form.
 376 | 
 377 | [fields(resource)](AshPhoenix.FilterForm.html#fields/1)
 378 | 
 379 | Returns the list of available fields, which may be attributes, calculations, or aggregates.
 380 | 
 381 | [filter(query, form)](AshPhoenix.FilterForm.html#filter/2)
 382 | 
 383 | Converts the form into a filter, and filters the provided query or resource with that filter.
 384 | 
 385 | [filter!(query, form)](AshPhoenix.FilterForm.html#filter!/2)
 386 | 
 387 | Same as [`filter/2`](AshPhoenix.FilterForm.html#filter/2) but raises on errors.
 388 | 
 389 | [new(resource, opts \\\\ \[\])](AshPhoenix.FilterForm.html#new/2)
 390 | 
 391 | Create a new filter form.
 392 | 
 393 | [params\_for\_query(predicate)](AshPhoenix.FilterForm.html#params_for_query/1)
 394 | 
 395 | Returns the minimal set of params (at the moment just strips ids) for use in a query string.
 396 | 
 397 | [predicates(resource)](AshPhoenix.FilterForm.html#predicates/1)
 398 | 
 399 | Returns the list of available predicates for the given resource, which may be functions or operators.
 400 | 
 401 | [raw\_errors(predicate)](AshPhoenix.FilterForm.html#raw_errors/1)
 402 | 
 403 | Returns a flat list of all errors on all predicates in the filter, without transforming.
 404 | 
 405 | [remove\_component(form, group\_or\_predicate\_id)](AshPhoenix.FilterForm.html#remove_component/2)
 406 | 
 407 | Removes the group *or* predicate with the given id
 408 | 
 409 | [remove\_group(form, group\_id)](AshPhoenix.FilterForm.html#remove_group/2)
 410 | 
 411 | Remove the group with the given id
 412 | 
 413 | [remove\_predicate(form, id)](AshPhoenix.FilterForm.html#remove_predicate/2)
 414 | 
 415 | Remove the predicate with the given id
 416 | 
 417 | [to\_filter!(form)](AshPhoenix.FilterForm.html#to_filter!/1) deprecated
 418 | 
 419 | [to\_filter\_expression(form)](AshPhoenix.FilterForm.html#to_filter_expression/1)
 420 | 
 421 | Returns a filter expression that can be provided to Ash.Query.filter/2
 422 | 
 423 | [to\_filter\_expression!(form)](AshPhoenix.FilterForm.html#to_filter_expression!/1)
 424 | 
 425 | Same as [`to_filter_expression/1`](AshPhoenix.FilterForm.html#to_filter_expression/1) but raises on errors.
 426 | 
 427 | [to\_filter\_map(form)](AshPhoenix.FilterForm.html#to_filter_map/1)
 428 | 
 429 | Returns a filter map that can be provided to `Ash.Filter.parse`
 430 | 
 431 | [update\_predicate(form, id, func)](AshPhoenix.FilterForm.html#update_predicate/3)
 432 | 
 433 | Update the predicate with the given id
 434 | 
 435 | [validate(form, params \\\\ %{}, opts \\\\ \[\])](AshPhoenix.FilterForm.html#validate/3)
 436 | 
 437 | Updates the filter with the provided input and validates it.
 438 | 
 439 | # [](AshPhoenix.FilterForm.html#functions)Functions
 440 | 
 441 | [](AshPhoenix.FilterForm.html#add_group/2)
 442 | 
 443 | # add\_group(form, opts \\\\ \[])
 444 | 
 445 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/filter_form/filter_form.ex#L1124)
 446 | 
 447 | Add a group to the filter. A group can contain predicates and other groups, allowing you to build quite complex nested filters.
 448 | 
 449 | Options:
 450 | 
 451 | - `:to` ([`String.t/0`](../elixir/String.html#t:t/0)) - The nested group id to add the group to.
 452 | - `:operator` - The operator that the group should have internally. Valid values are :and, :or The default value is `:and`.
 453 | - `:return_id?` ([`boolean/0`](../elixir/typespecs.html#built-in-types)) - If set to `true`, the function returns `{form, predicate_id}` The default value is `false`.
 454 | 
 455 | [](AshPhoenix.FilterForm.html#add_predicate/5)
 456 | 
 457 | # add\_predicate(form, field, operator\_or\_function, value, opts \\\\ \[])
 458 | 
 459 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/filter_form/filter_form.ex#L961)
 460 | 
 461 | Add a predicate to the filter.
 462 | 
 463 | Options:
 464 | 
 465 | - `:to` ([`String.t/0`](../elixir/String.html#t:t/0)) - The group id to add the predicate to. If not set, will be added to the top level group.
 466 | - `:return_id?` ([`boolean/0`](../elixir/typespecs.html#built-in-types)) - If set to `true`, the function returns `{form, predicate_id}` The default value is `false`.
 467 | - `:path` - The relationship path to apply the predicate to
 468 | 
 469 | [](AshPhoenix.FilterForm.html#errors/2)
 470 | 
 471 | # errors(form, opts \\\\ \[])
 472 | 
 473 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/filter_form/filter_form.ex#L468)
 474 | 
 475 | Returns a flat list of all errors on all predicates in the filter, made safe for display in a form.
 476 | 
 477 | Only errors that implement the [`AshPhoenix.FormData.Error`](AshPhoenix.FormData.Error.html) protocol are displayed.
 478 | 
 479 | [](AshPhoenix.FilterForm.html#fields/1)
 480 | 
 481 | # fields(resource)
 482 | 
 483 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/filter_form/filter_form.ex#L929)
 484 | 
 485 | Returns the list of available fields, which may be attributes, calculations, or aggregates.
 486 | 
 487 | [](AshPhoenix.FilterForm.html#filter/2)
 488 | 
 489 | # filter(query, form)
 490 | 
 491 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/filter_form/filter_form.ex#L623)
 492 | 
 493 | Converts the form into a filter, and filters the provided query or resource with that filter.
 494 | 
 495 | [](AshPhoenix.FilterForm.html#filter!/2)
 496 | 
 497 | # filter!(query, form)
 498 | 
 499 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/filter_form/filter_form.ex#L636)
 500 | 
 501 | Same as [`filter/2`](AshPhoenix.FilterForm.html#filter/2) but raises on errors.
 502 | 
 503 | [](AshPhoenix.FilterForm.html#new/2)
 504 | 
 505 | # new(resource, opts \\\\ \[])
 506 | 
 507 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/filter_form/filter_form.ex#L251)
 508 | 
 509 | Create a new filter form.
 510 | 
 511 | Options:
 512 | 
 513 | - `:params` ([`term/0`](../elixir/typespecs.html#built-in-types)) - Initial parameters to create the form with The default value is `%{}`.
 514 | - `:as` ([`String.t/0`](../elixir/String.html#t:t/0)) - Set the parameter name for the form. The default value is `"filter"`.
 515 | - `:transform_errors` ([`term/0`](../elixir/typespecs.html#built-in-types)) - Allows for manual manipulation and transformation of errors.  
 516 |   If possible, try to implement [`AshPhoenix.FormData.Error`](AshPhoenix.FormData.Error.html) 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 predicate and the error, and should return a list of ash phoenix formatted errors, e.g `[{field :: atom, message :: String.t(), substituations :: Keyword.t()}]`
 517 | - `:warn_on_unhandled_errors?` ([`boolean/0`](../elixir/typespecs.html#built-in-types)) - Whether or not to emit warning log on unhandled form errors The default value is `true`.
 518 | - `:remove_empty_groups?` ([`boolean/0`](../elixir/typespecs.html#built-in-types)) - If true (the default), then any time a group would be made empty by removing a group or predicate, it is removed instead.  
 519 |   An empty form can still be added, this only affects a group if its last component is removed. The default value is `false`.
 520 | 
 521 | [](AshPhoenix.FilterForm.html#params_for_query/1)
 522 | 
 523 | # params\_for\_query(predicate)
 524 | 
 525 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/filter_form/filter_form.ex#L849)
 526 | 
 527 | Returns the minimal set of params (at the moment just strips ids) for use in a query string.
 528 | 
 529 | [](AshPhoenix.FilterForm.html#predicates/1)
 530 | 
 531 | # predicates(resource)
 532 | 
 533 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/filter_form/filter_form.ex#L911)
 534 | 
 535 | Returns the list of available predicates for the given resource, which may be functions or operators.
 536 | 
 537 | [](AshPhoenix.FilterForm.html#raw_errors/1)
 538 | 
 539 | # raw\_errors(predicate)
 540 | 
 541 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/filter_form/filter_form.ex#L483)
 542 | 
 543 | Returns a flat list of all errors on all predicates in the filter, without transforming.
 544 | 
 545 | [](AshPhoenix.FilterForm.html#remove_component/2)
 546 | 
 547 | # remove\_component(form, group\_or\_predicate\_id)
 548 | 
 549 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/filter_form/filter_form.ex#L1187)
 550 | 
 551 | Removes the group *or* predicate with the given id
 552 | 
 553 | [](AshPhoenix.FilterForm.html#remove_group/2)
 554 | 
 555 | # remove\_group(form, group\_id)
 556 | 
 557 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/filter_form/filter_form.ex#L1166)
 558 | 
 559 | Remove the group with the given id
 560 | 
 561 | [](AshPhoenix.FilterForm.html#remove_predicate/2)
 562 | 
 563 | # remove\_predicate(form, id)
 564 | 
 565 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/filter_form/filter_form.ex#L1027)
 566 | 
 567 | Remove the predicate with the given id
 568 | 
 569 | [](AshPhoenix.FilterForm.html#to_filter!/1)
 570 | 
 571 | # to\_filter!(form)
 572 | 
 573 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/filter_form/filter_form.ex#L461)
 574 | 
 575 | This function is deprecated. Use to\_filter\_expression!/1 instead.
 576 | 
 577 | [](AshPhoenix.FilterForm.html#to_filter_expression/1)
 578 | 
 579 | # to\_filter\_expression(form)
 580 | 
 581 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/filter_form/filter_form.ex#L422)
 582 | 
 583 | Returns a filter expression that can be provided to Ash.Query.filter/2
 584 | 
 585 | To add this to a query, remember to use `^`, for example:
 586 | 
 587 | ```
 588 | filter = AshPhoenix.FilterForm.to_filter_expression(form)
 589 | 
 590 | Ash.Query.filter(MyApp.Post, ^filter)
 591 | ```
 592 | 
 593 | Alternatively, you can use the shorthand: [`filter/2`](AshPhoenix.FilterForm.html#filter/2) to apply the expression directly to a query.
 594 | 
 595 | [](AshPhoenix.FilterForm.html#to_filter_expression!/1)
 596 | 
 597 | # to\_filter\_expression!(form)
 598 | 
 599 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/filter_form/filter_form.ex#L442)
 600 | 
 601 | Same as [`to_filter_expression/1`](AshPhoenix.FilterForm.html#to_filter_expression/1) but raises on errors.
 602 | 
 603 | [](AshPhoenix.FilterForm.html#to_filter_map/1)
 604 | 
 605 | # to\_filter\_map(form)
 606 | 
 607 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/filter_form/filter_form.ex#L324)
 608 | 
 609 | Returns a filter map that can be provided to `Ash.Filter.parse`
 610 | 
 611 | This allows for things like saving a stored filter. Does not currently support parameterizing calculations or functions.
 612 | 
 613 | [](AshPhoenix.FilterForm.html#update_predicate/3)
 614 | 
 615 | # update\_predicate(form, id, func)
 616 | 
 617 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/filter_form/filter_form.ex#L1048)
 618 | 
 619 | Update the predicate with the given id
 620 | 
 621 | [](AshPhoenix.FilterForm.html#validate/3)
 622 | 
 623 | # validate(form, params \\\\ %{}, opts \\\\ \[])
 624 | 
 625 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/filter_form/filter_form.ex#L295)
 626 | 
 627 | Updates the filter with the provided input and validates it.
 628 | 
 629 | At present, no validation actually occurs, but this will eventually be added.
 630 | 
 631 | Passing `reset_on_change?: false` into `opts` will prevent predicates to reset the `value` and `operator` fields to `nil` if the predicate `field` changes.
 632 | 
 633 | [Hex Package](https://hex.pm/packages/ash_phoenix/2.1.14) [Hex Preview](https://preview.hex.pm/preview/ash_phoenix/2.1.14) Search HexDocs [Download ePub version](ash_phoenix.epub "ePub version")
 634 | 
 635 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.36.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
 636 | [![ash_phoenix](assets/logo.png)](https://github.com/ash-project/ash_phoenix)
 637 | 
 638 | [ash\_phoenix](https://github.com/ash-project/ash_phoenix)
 639 | 
 640 | v2.1.14
 641 | 
 642 | - Pages
 643 | - Modules
 644 | - Mix Tasks
 645 | 
 646 | <!--THE END-->
 647 | 
 648 | <!--THE END-->
 649 | 
 650 | <!--THE END-->
 651 | 
 652 | Search documentation of ash\_phoenix
 653 | 
 654 | Settings
 655 | 
 656 | # [View Source](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/filter_form/predicate.ex#L1 "View Source") AshPhoenix.FilterForm.Predicate (ash\_phoenix v2.1.14)
 657 | 
 658 | Represents an individual predicate appearing in a filter form.
 659 | 
 660 | Predicates are grouped up in an [`AshPhoenix.FilterForm`](AshPhoenix.FilterForm.html) to create boolean filter statements.
 661 | 
 662 | # [](AshPhoenix.FilterForm.Predicate.html#summary)Summary
 663 | 
 664 | ## [Functions](AshPhoenix.FilterForm.Predicate.html#functions)
 665 | 
 666 | [errors(predicate, transform\_errors)](AshPhoenix.FilterForm.Predicate.html#errors/2)
 667 | 
 668 | # [](AshPhoenix.FilterForm.Predicate.html#functions)Functions
 669 | 
 670 | [](AshPhoenix.FilterForm.Predicate.html#errors/2)
 671 | 
 672 | # errors(predicate, transform\_errors)
 673 | 
 674 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/filter_form/predicate.ex#L23)
 675 | 
 676 | [Hex Package](https://hex.pm/packages/ash_phoenix/2.1.14) [Hex Preview](https://preview.hex.pm/preview/ash_phoenix/2.1.14) Search HexDocs [Download ePub version](ash_phoenix.epub "ePub version")
 677 | 
 678 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.36.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
 679 | [![ash_phoenix](assets/logo.png)](https://github.com/ash-project/ash_phoenix)
 680 | 
 681 | [ash\_phoenix](https://github.com/ash-project/ash_phoenix)
 682 | 
 683 | v2.1.14
 684 | 
 685 | - Pages
 686 | - Modules
 687 | - Mix Tasks
 688 | 
 689 | <!--THE END-->
 690 | 
 691 | <!--THE END-->
 692 | 
 693 | <!--THE END-->
 694 | 
 695 | Search documentation of ash\_phoenix
 696 | 
 697 | Settings
 698 | 
 699 | # [View Source](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/auto.ex#L1 "View Source") AshPhoenix.Form.Auto (ash\_phoenix v2.1.14)
 700 | 
 701 | A tool to automatically generate available nested forms based on a resource and action.
 702 | 
 703 | To use this, specify `forms: [auto?: true]` when creating the form.
 704 | 
 705 | Keep in mind, you can always specify these manually when creating a form by simply specifying the `forms` option.
 706 | 
 707 | There are two things that this builds forms for:
 708 | 
 709 | 1. Attributes/arguments who's type is an embedded resource.
 710 | 2. Arguments that have a corresponding `change manage_relationship(..)` configured.
 711 | 
 712 | For more on relationships see the documentation for [`Ash.Changeset.manage_relationship/4`](../ash/3.4.55/Ash.Changeset.html#manage_relationship/4).
 713 | 
 714 | When building forms, you can switch on the action type and/or resource of the form, in order to have different fields depending on the form. For example, if you have a simple relationship called `:comments` with `on_match: :update` and `on_no_match: :create`, there are two types of forms that can be in `inputs_for(form, :comments)`.
 715 | 
 716 | In which case you may have something like this:
 717 | 
 718 | ```
 719 | <%= for comment_form <- inputs_for(f, :comments) do %>
 720 |   <%= hidden_inputs_for(comment_form) %>
 721 |   <%= if comment_form.source.type == :create do %>
 722 |     <%= text_input comment_form, :text %>
 723 |     <%= text_input comment_form, :on_create_field %>
 724 |   <% else %>
 725 |     <%= text_input comment_form, :text %>
 726 |     <%= text_input comment_form, :on_update_field %>
 727 |   <% end %>
 728 | 
 729 |   <button phx-click="remove_form" phx-value-path="<%= comment_form.name %>">Remove Comment</button>
 730 |   <button phx-click="add_form" phx-value-path="<%= comment_form.name %>">Add Comment</button>
 731 | <% end %>
 732 | ```
 733 | 
 734 | This also applies to adding forms of different types manually. For instance, if you had a "search" field to allow them to search for a record (e.g in a liveview), and you had an `on_lookup` read action, you could render a search form for that read action, and once they've selected a record, you could render the fields to update that record (in the case of `on_lookup: :relate_and_update` configurations).
 735 | 
 736 | ## [](AshPhoenix.Form.Auto.html#module-options)Options
 737 | 
 738 | - `:relationship_fetcher` ([`term/0`](../elixir/typespecs.html#built-in-types)) - A two argument function that receives the parent data, the relationship to fetch. The default simply fetches the relationship value, and if it isn't loaded, it uses `[]` or `nil`.
 739 | - `:sparse_lists?` ([`boolean/0`](../elixir/typespecs.html#built-in-types)) - Sets all list type forms to `sparse?: true` by default. Has no effect on forms derived for embedded resources. The default value is `false`.
 740 | - `:include_non_map_types?` ([`boolean/0`](../elixir/typespecs.html#built-in-types)) - Creates form for non map or array of map type inputs The default value is `false`.
 741 | 
 742 | ## [](AshPhoenix.Form.Auto.html#module-special-considerations)Special Considerations
 743 | 
 744 | ### [](AshPhoenix.Form.Auto.html#module-on_lookup-relate_and_update)`on_lookup: :relate_and_update`
 745 | 
 746 | For `on_lookup: :relate_and_update` configurations, the "read" form for that relationship will use the appropriate read action. However, you may also want to include the relevant fields for the update that would subsequently occur. To that end, a special nested form called `:_update` is created, that uses an empty instance of that resource as the base of its changeset. This may require some manual manipulation of that data before rendering the relevant form because it assumes all the default values. To solve for this, if you are using liveview, you could actually look up the record using the input from the read action, and then use [`AshPhoenix.Form.update_form/3`](AshPhoenix.Form.html#update_form/3) to set that looked up record as the data of the `_update` form.
 747 | 
 748 | ### [](AshPhoenix.Form.Auto.html#module-many-to-many-relationships)Many to Many Relationships
 749 | 
 750 | In the case that a manage\_change option points to a join relationship, that form is presented via a special nested form called `_join`. So the first form in `inputs_for(form, :relationship)` would be for the destination, and then inside of that you could say `inputs_for(nested_form, :_join)`. The parameters are merged together during submission.
 751 | 
 752 | # [](AshPhoenix.Form.Auto.html#summary)Summary
 753 | 
 754 | ## [Functions](AshPhoenix.Form.Auto.html#functions)
 755 | 
 756 | [auto(resource, action, opts \\\\ \[\])](AshPhoenix.Form.Auto.html#auto/3)
 757 | 
 758 | [embedded(resource, action, auto\_opts)](AshPhoenix.Form.Auto.html#embedded/3)
 759 | 
 760 | [related(resource, action, auto\_opts)](AshPhoenix.Form.Auto.html#related/3)
 761 | 
 762 | [unions(resource, action, auto\_opts)](AshPhoenix.Form.Auto.html#unions/3)
 763 | 
 764 | # [](AshPhoenix.Form.Auto.html#functions)Functions
 765 | 
 766 | [](AshPhoenix.Form.Auto.html#auto/3)
 767 | 
 768 | # auto(resource, action, opts \\\\ \[])
 769 | 
 770 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/auto.ex#L88)
 771 | 
 772 | [](AshPhoenix.Form.Auto.html#embedded/3)
 773 | 
 774 | # embedded(resource, action, auto\_opts)
 775 | 
 776 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/auto.ex#L803)
 777 | 
 778 | [](AshPhoenix.Form.Auto.html#related/3)
 779 | 
 780 | # related(resource, action, auto\_opts)
 781 | 
 782 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/auto.ex#L362)
 783 | 
 784 | [](AshPhoenix.Form.Auto.html#unions/3)
 785 | 
 786 | # unions(resource, action, auto\_opts)
 787 | 
 788 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/auto.ex#L97)
 789 | 
 790 | [Hex Package](https://hex.pm/packages/ash_phoenix/2.1.14) [Hex Preview](https://preview.hex.pm/preview/ash_phoenix/2.1.14) Search HexDocs [Download ePub version](ash_phoenix.epub "ePub version")
 791 | 
 792 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.36.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
 793 | [![ash_phoenix](assets/logo.png)](https://github.com/ash-project/ash_phoenix)
 794 | 
 795 | [ash\_phoenix](https://github.com/ash-project/ash_phoenix)
 796 | 
 797 | v2.1.14
 798 | 
 799 | - Pages
 800 | - Modules
 801 | - Mix Tasks
 802 | 
 803 | <!--THE END-->
 804 | 
 805 | <!--THE END-->
 806 | 
 807 | <!--THE END-->
 808 | 
 809 | Search documentation of ash\_phoenix
 810 | 
 811 | Settings
 812 | 
 813 | # [View Source](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form_data/error.ex#L1 "View Source") AshPhoenix.FormData.Error protocol (ash\_phoenix v2.1.14)
 814 | 
 815 | A protocol for allowing errors to be rendered into a form.
 816 | 
 817 | To implement, define a [`to_form_error/1`](AshPhoenix.FormData.Error.html#to_form_error/1) and return a single error or list of errors of the following shape:
 818 | 
 819 | `{:field_name, message, replacements}`
 820 | 
 821 | Replacements is a keyword list to allow for translations, by extracting out the constants like numbers from the message.
 822 | 
 823 | # [](AshPhoenix.FormData.Error.html#summary)Summary
 824 | 
 825 | ## [Types](AshPhoenix.FormData.Error.html#types)
 826 | 
 827 | [t()](AshPhoenix.FormData.Error.html#t:t/0)
 828 | 
 829 | All the types that implement this protocol.
 830 | 
 831 | ## [Functions](AshPhoenix.FormData.Error.html#functions)
 832 | 
 833 | [to\_form\_error(exception)](AshPhoenix.FormData.Error.html#to_form_error/1)
 834 | 
 835 | # [](AshPhoenix.FormData.Error.html#types)Types
 836 | 
 837 | [](AshPhoenix.FormData.Error.html#t:t/0)
 838 | 
 839 | # t()
 840 | 
 841 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form_data/error.ex#L1)
 842 | 
 843 | ```
 844 | @type t() :: term()
 845 | ```
 846 | 
 847 | All the types that implement this protocol.
 848 | 
 849 | # [](AshPhoenix.FormData.Error.html#functions)Functions
 850 | 
 851 | [](AshPhoenix.FormData.Error.html#to_form_error/1)
 852 | 
 853 | # to\_form\_error(exception)
 854 | 
 855 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form_data/error.ex#L12)
 856 | 
 857 | [Hex Package](https://hex.pm/packages/ash_phoenix/2.1.14) [Hex Preview](https://preview.hex.pm/preview/ash_phoenix/2.1.14) Search HexDocs [Download ePub version](ash_phoenix.epub "ePub version")
 858 | 
 859 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.36.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
 860 | [![ash_phoenix](assets/logo.png)](https://github.com/ash-project/ash_phoenix)
 861 | 
 862 | [ash\_phoenix](https://github.com/ash-project/ash_phoenix)
 863 | 
 864 | v2.1.14
 865 | 
 866 | - Pages
 867 | - Modules
 868 | - Mix Tasks
 869 | 
 870 | <!--THE END-->
 871 | 
 872 | <!--THE END-->
 873 | 
 874 | <!--THE END-->
 875 | 
 876 | Search documentation of ash\_phoenix
 877 | 
 878 | Settings
 879 | 
 880 | # [View Source](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/form.ex#L1 "View Source") AshPhoenix.Form (ash\_phoenix v2.1.14)
 881 | 
 882 | A module to allow you to fluidly use resources with Phoenix forms.
 883 | 
 884 | ### [](AshPhoenix.Form.html#module-life-cycle)Life cycle
 885 | 
 886 | The general workflow is, with either LiveView or Phoenix forms:
 887 | 
 888 | 1. Create a form with [`AshPhoenix.Form`](AshPhoenix.Form.html)
 889 | 2. Render the form with `Phoenix.Component.form` (or `CoreComponents.simple_form`), or, if using Surface, `<Form>`
 890 | 3. To validate the form (e.g with `phx-change` for liveview), pass the submitted params to [`AshPhoenix.Form.validate/3`](AshPhoenix.Form.html#validate/3)
 891 | 4. On form submission, pass the params to [`AshPhoenix.Form.submit/2`](AshPhoenix.Form.html#submit/2)
 892 | 5. On success, use the result to redirect or assign. On failure, reassign the provided form.
 893 | 
 894 | The following keys exist on the form to show where in the lifecycle you are:
 895 | 
 896 | - `submitted_once?` - If the form has ever been submitted. Useful for not showing any errors on the first attempt to fill out a form.
 897 | - `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.
 898 | - `.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.
 899 | - `.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.
 900 | 
 901 | ### [](AshPhoenix.Form.html#module-forms-in-the-code-interface)Forms in the code interface
 902 | 
 903 | Throughout this documentation you will see forms created with [`AshPhoenix.Form.for_create/3`](AshPhoenix.Form.html#for_create/3) and other functions like it. This is perfectly fine to do, however there is a way to use [`AshPhoenix.Form`](AshPhoenix.Form.html) 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:
 904 | 
 905 | ```
 906 | resources do
 907 |   resource MyApp.Accounts.User do
 908 |     define :register_with_password, args: [:email, :password]
 909 |     define :update_user, action: :update, args: [:email, :password]
 910 |   end
 911 | end
 912 | ```
 913 | 
 914 | Adding the [`AshPhoenix`](AshPhoenix.html) extension to our domains and resources, like so:
 915 | 
 916 | ```
 917 | use Ash.Domain,
 918 |   extensions: [AshPhoenix]
 919 | ```
 920 | 
 921 | will cause another function to be generated for each definition, beginning with `form_to_`.
 922 | 
 923 | With this extension, the standard setup for forms looks something like this:
 924 | 
 925 | ```
 926 | def render(assigns) do
 927 |   ~H"""
 928 |   <.form for={@form} phx-change="validate" phx-submit="submit">
 929 |     <.input field={@form[:email]} />
 930 |     <.input field={@form[:password]} />
 931 |     <.button type="submit" />
 932 |   </.form>
 933 |   """
 934 | end
 935 | 
 936 | def mount(_params, _session, socket) do
 937 |   # Here we call our new generated function to create the form
 938 |   {:ok, assign(socket, form: MyApp.Accounts.form_to_register_with_password())}
 939 | end
 940 | 
 941 | def handle_event(socket, "validate", %{"form" => params}) do
 942 |   form = AshPhoenix.Form.validate(socket.assigns.form, params)
 943 |   {:noreply, assign(socket, :form, form)}
 944 | end
 945 | 
 946 | def handle_event(socket, "submit", %{"form" => params}) do
 947 |   case AshPhoenix.Form.submit(socket.assigns.form, params: params) do
 948 |     {:ok, _user} ->
 949 |       socket =
 950 |         socket
 951 |         |> put_flash(:success, "User registered successfully")
 952 |         |> push_navigate(to: ~p"/")
 953 | 
 954 |       {:noreply, socket}
 955 | 
 956 |     {:error, form} ->
 957 |       socket =
 958 |         socket
 959 |         |> put_flash(:error, "Something went wrong")
 960 |         |> assign(:form, form)
 961 | 
 962 |       {:noreply, socket}
 963 |   end
 964 | end
 965 | ```
 966 | 
 967 | ## [](AshPhoenix.Form.html#module-working-with-related-or-embedded-data)Working with related or embedded data
 968 | 
 969 | See the [nested forms guide](nested-forms.html)
 970 | 
 971 | ## [](AshPhoenix.Form.html#module-working-with-compound-types)Working with compound types
 972 | 
 973 | Compound types, such as `Ash.Money`, will need some extra work to make it work.
 974 | 
 975 | 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:
 976 | 
 977 | ```
 978 | <.input
 979 |     name={@form[:amount].name <> "[amount]"}
 980 |     id={@form[:amount].id <> "_amount"}
 981 |     label="Amount"
 982 |     value={if(@form[:amount].value, do: @form[:amount].value.amount)}
 983 |   />
 984 |   <.input
 985 |     type="select"
 986 |     name={@form[:amount].name <> "[currency]"}
 987 |     id={@form[:amount].id <> "_currency"}
 988 |     options={[:USD, :HKD, :EUR]}
 989 |     label="Currency"
 990 |     value={if(@form[:amount].value, do: @form[:amount].value.currency)}
 991 |   />
 992 | ```
 993 | 
 994 | The above will allow the fields to be used by the [`AshPhoenix.Form`](AshPhoenix.Form.html) when creating or updating a Transfer. You can follow the same style with other compound types.
 995 | 
 996 | # [](AshPhoenix.Form.html#summary)Summary
 997 | 
 998 | ## [Types](AshPhoenix.Form.html#types)
 999 | 
1000 | [path()](AshPhoenix.Form.html#t:path/0)
1001 | 
1002 | [source()](AshPhoenix.Form.html#t:source/0)
1003 | 
1004 | [t()](AshPhoenix.Form.html#t:t/0)
1005 | 
1006 | ## [Functions](AshPhoenix.Form.html#functions)
1007 | 
1008 | [add\_error(form, error, opts \\\\ \[\])](AshPhoenix.Form.html#add_error/3)
1009 | 
1010 | Adds an error to the source underlying the form.
1011 | 
1012 | [add\_form(form, path, opts \\\\ \[\])](AshPhoenix.Form.html#add_form/3)
1013 | 
1014 | Adds a new form at the provided path.
1015 | 
1016 | [arguments(form)](AshPhoenix.Form.html#arguments/1)
1017 | 
1018 | A utility to get the list of arguments the action underlying the form accepts
1019 | 
1020 | [ash\_errors(form, opts \\\\ \[\])](AshPhoenix.Form.html#ash_errors/2)
1021 | 
1022 | [attributes(form)](AshPhoenix.Form.html#attributes/1)
1023 | 
1024 | A utility to get the list of attributes the action underlying the form accepts
1025 | 
1026 | [can\_submit?(form)](AshPhoenix.Form.html#can_submit?/1)
1027 | 
1028 | [clear\_value(form, field\_or\_fields)](AshPhoenix.Form.html#clear_value/2)
1029 | 
1030 | Clears a given input's value on a form.
1031 | 
1032 | [ensure\_can\_submit!(form)](AshPhoenix.Form.html#ensure_can_submit!/1)
1033 | 
1034 | [errors(form, opts \\\\ \[\])](AshPhoenix.Form.html#errors/2)
1035 | 
1036 | Returns the errors on the form.
1037 | 
1038 | [for\_action(resource\_or\_data, action, opts \\\\ \[\])](AshPhoenix.Form.html#for_action/3)
1039 | 
1040 | Creates a form corresponding to any given action on a resource.
1041 | 
1042 | [for\_create(resource, action, opts \\\\ \[\])](AshPhoenix.Form.html#for_create/3)
1043 | 
1044 | Creates a form corresponding to a create action on a resource.
1045 | 
1046 | [for\_destroy(data, action, opts \\\\ \[\])](AshPhoenix.Form.html#for_destroy/3)
1047 | 
1048 | Creates a form corresponding to a destroy action on a record.
1049 | 
1050 | [for\_read(resource, action, opts \\\\ \[\])](AshPhoenix.Form.html#for_read/3)
1051 | 
1052 | Creates a form corresponding to a read action on a resource.
1053 | 
1054 | [for\_update(data, action, opts \\\\ \[\])](AshPhoenix.Form.html#for_update/3)
1055 | 
1056 | Creates a form corresponding to an update action on a record.
1057 | 
1058 | [get\_form(form, path)](AshPhoenix.Form.html#get_form/2)
1059 | 
1060 | Gets the form at the specified path
1061 | 
1062 | [has\_form?(form, path)](AshPhoenix.Form.html#has_form?/2)
1063 | 
1064 | Returns true if a given form path exists in the form
1065 | 
1066 | [hidden\_fields(form)](AshPhoenix.Form.html#hidden_fields/1)
1067 | 
1068 | Returns the hidden fields for a form as a keyword list
1069 | 
1070 | [ignore(form)](AshPhoenix.Form.html#ignore/1)
1071 | 
1072 | Toggles the form to be ignored or not ignored.
1073 | 
1074 | [ignored?(form)](AshPhoenix.Form.html#ignored?/1)
1075 | 
1076 | Returns true if the form is ignored
1077 | 
1078 | [merge\_options(form, opts)](AshPhoenix.Form.html#merge_options/2)
1079 | 
1080 | Merge the new options with the saved options on a form. See [`update_options/2`](AshPhoenix.Form.html#update_options/2) for more.
1081 | 
1082 | [params(form, opts \\\\ \[\])](AshPhoenix.Form.html#params/2)
1083 | 
1084 | Returns the parameters from the form that would be submitted to the action.
1085 | 
1086 | [parse\_path!(form, original\_path, opts \\\\ \[\])](AshPhoenix.Form.html#parse_path!/3)
1087 | 
1088 | A utility for parsing paths of nested forms in query encoded format.
1089 | 
1090 | [remove\_form(form, path, opts \\\\ \[\])](AshPhoenix.Form.html#remove_form/3)
1091 | 
1092 | Removes a form at the provided path.
1093 | 
1094 | [set\_data(form, data)](AshPhoenix.Form.html#set_data/2)
1095 | 
1096 | Sets the data of the form, in addition to the data of the underlying source, if applicable.
1097 | 
1098 | [sort\_forms(form, path, instruction)](AshPhoenix.Form.html#sort_forms/3)
1099 | 
1100 | Sorts nested forms at the given path.
1101 | 
1102 | [submit(form, opts \\\\ \[\])](AshPhoenix.Form.html#submit/2)
1103 | 
1104 | Submits the form.
1105 | 
1106 | [submit!(form, opts \\\\ \[\])](AshPhoenix.Form.html#submit!/2)
1107 | 
1108 | Same as [`submit/2`](AshPhoenix.Form.html#submit/2), but raises an error if the submission fails.
1109 | 
1110 | [touch(form, fields)](AshPhoenix.Form.html#touch/2)
1111 | 
1112 | Mark a field or fields as touched
1113 | 
1114 | [update\_form(form, path, func, opts \\\\ \[\])](AshPhoenix.Form.html#update_form/4)
1115 | 
1116 | Updates the form at the provided path using the given function.
1117 | 
1118 | [update\_forms\_at\_path(form, path, func, opts \\\\ \[\])](AshPhoenix.Form.html#update_forms_at_path/4)
1119 | 
1120 | Updates the list of forms matching a given path. Does not validate that the path points at a single form like [`update_form/4`](AshPhoenix.Form.html#update_form/4).
1121 | 
1122 | [update\_options(form, fun)](AshPhoenix.Form.html#update_options/2)
1123 | 
1124 | Update the saved options on a form.
1125 | 
1126 | [update\_params(form, func, validate\_opts \\\\ \[\])](AshPhoenix.Form.html#update_params/3)
1127 | 
1128 | Update the previous params provided to the form, and revalidate.
1129 | 
1130 | [validate(form, new\_params, opts \\\\ \[\])](AshPhoenix.Form.html#validate/3)
1131 | 
1132 | Validates the parameters against the form.
1133 | 
1134 | [value(form, field)](AshPhoenix.Form.html#value/2)
1135 | 
1136 | Gets the value for a given field in the form.
1137 | 
1138 | # [](AshPhoenix.Form.html#types)Types
1139 | 
1140 | [](AshPhoenix.Form.html#t:path/0)
1141 | 
1142 | # path()
1143 | 
1144 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/form.ex#L182)
1145 | 
1146 | ```
1147 | @type path() :: String.t() | atom() | [String.t() | atom() | integer()]
1148 | ```
1149 | 
1150 | [](AshPhoenix.Form.html#t:source/0)
1151 | 
1152 | # source()
1153 | 
1154 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/form.ex#L156)
1155 | 
1156 | ```
1157 | @type source() :: Ash.Changeset.t() | Ash.Query.t() | Ash.Resource.record()
1158 | ```
1159 | 
1160 | [](AshPhoenix.Form.html#t:t/0)
1161 | 
1162 | # t()
1163 | 
1164 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/form.ex#L158)
1165 | 
1166 | ```
1167 | @type t() :: %AshPhoenix.Form{
1168 |   action: atom(),
1169 |   added?: term(),
1170 |   any_removed?: term(),
1171 |   changed?: term(),
1172 |   data: nil | Ash.Resource.record(),
1173 |   domain: term(),
1174 |   errors: boolean(),
1175 |   form_keys: Keyword.t(),
1176 |   forms: map(),
1177 |   id: term(),
1178 |   just_submitted?: boolean(),
1179 |   method: String.t(),
1180 |   name: term(),
1181 |   opts: Keyword.t(),
1182 |   original_data: term(),
1183 |   params: map(),
1184 |   prepare_params: term(),
1185 |   prepare_source: nil | (source() -> source()),
1186 |   raw_params: term(),
1187 |   resource: Ash.Resource.t(),
1188 |   source: source(),
1189 |   submit_errors: Keyword.t() | nil,
1190 |   submitted_once?: boolean(),
1191 |   touched_forms: term(),
1192 |   transform_errors:
1193 |     nil
1194 |     | (source(), error :: Ash.Error.t() ->
1195 |          [
1196 |            {field :: atom(), message :: String.t(),
1197 |             substituations :: Keyword.t()}
1198 |          ]),
1199 |   transform_params: nil | (map() -> term()),
1200 |   type: :create | :update | :destroy | :read,
1201 |   valid?: boolean(),
1202 |   warn_on_unhandled_errors?: term()
1203 | }
1204 | ```
1205 | 
1206 | # [](AshPhoenix.Form.html#functions)Functions
1207 | 
1208 | [](AshPhoenix.Form.html#add_error/3)
1209 | 
1210 | # add\_error(form, error, opts \\\\ \[])
1211 | 
1212 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/form.ex#L3399)
1213 | 
1214 | Adds an error to the source underlying the form.
1215 | 
1216 | 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.
1217 | 
1218 | See [`Ash.Error.to_ash_error/3`](../ash/3.4.55/Ash.Error.html#to_ash_error/3) for more on supported values for `error`.
1219 | 
1220 | # Options
1221 | 
1222 | - `:path` - The path to add the error to. If the error(s) already have a path, don't specify a path yourself.
1223 | 
1224 | [](AshPhoenix.Form.html#add_form/3)
1225 | 
1226 | # add\_form(form, path, opts \\\\ \[])
1227 | 
1228 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/form.ex#L3455)
1229 | 
1230 | ```
1231 | @spec add_form(t(), path(), Keyword.t()) :: t()
1232 | ```
1233 | 
1234 | ```
1235 | @spec add_form(Phoenix.HTML.Form.t(), path(), Keyword.t()) :: Phoenix.HTML.Form.t()
1236 | ```
1237 | 
1238 | Adds a new form at the provided path.
1239 | 
1240 | Doing this requires that the form has a `create_action` and a `resource` configured.
1241 | 
1242 | `path` can be one of two things:
1243 | 
1244 | 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.
1245 | 2. The html name of the form, e.g `form[posts][0][comments]` to mimic the above
1246 | 
1247 | 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`](AshPhoenix.Form.html#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`](AshPhoenix.Form.html#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).
1248 | 
1249 | - `:prepend` ([`boolean/0`](../elixir/typespecs.html#built-in-types)) - If specified, the form is placed at the beginning of the list instead of the end of the list The default value is `false`.
1250 | - `:params` ([`term/0`](../elixir/typespecs.html#built-in-types)) - The initial parameters to add the form with. The default value is `%{}`.
1251 | - `:validate?` ([`boolean/0`](../elixir/typespecs.html#built-in-types)) - Validates the new full form. The default value is `true`.
1252 | - `:validate_opts` ([`term/0`](../elixir/typespecs.html#built-in-types)) - Options to pass to `validate`. Only used if `validate?` is set to `true` (the default) The default value is `[]`.
1253 | - `: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`.
1254 | - `:data` ([`term/0`](../elixir/typespecs.html#built-in-types)) - 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.
1255 | 
1256 | [](AshPhoenix.Form.html#arguments/1)
1257 | 
1258 | # arguments(form)
1259 | 
1260 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/form.ex#L1021)
1261 | 
1262 | A utility to get the list of arguments the action underlying the form accepts
1263 | 
1264 | [](AshPhoenix.Form.html#ash_errors/2)
1265 | 
1266 | # ash\_errors(form, opts \\\\ \[])
1267 | 
1268 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/form.ex#L2726)
1269 | 
1270 | [](AshPhoenix.Form.html#attributes/1)
1271 | 
1272 | # attributes(form)
1273 | 
1274 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/form.ex#L1015)
1275 | 
1276 | A utility to get the list of attributes the action underlying the form accepts
1277 | 
1278 | [](AshPhoenix.Form.html#can_submit?/1)
1279 | 
1280 | # can\_submit?(form)
1281 | 
1282 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/form.ex#L705)
1283 | 
1284 | ```
1285 | @spec can_submit?(t()) :: boolean()
1286 | ```
1287 | 
1288 | ```
1289 | @spec can_submit?(Phoenix.HTML.Form.t()) :: boolean()
1290 | ```
1291 | 
1292 | [](AshPhoenix.Form.html#clear_value/2)
1293 | 
1294 | # clear\_value(form, field\_or\_fields)
1295 | 
1296 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/form.ex#L2912)
1297 | 
1298 | ```
1299 | @spec clear_value(t(), atom() | [atom()]) :: t()
1300 | ```
1301 | 
1302 | Clears a given input's value on a form.
1303 | 
1304 | Accepts a field (atom) or a list of fields (atoms) as a second argument.
1305 | 
1306 | [](AshPhoenix.Form.html#ensure_can_submit!/1)
1307 | 
1308 | # ensure\_can\_submit!(form)
1309 | 
1310 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/form.ex#L715)
1311 | 
1312 | ```
1313 | @spec ensure_can_submit!(t()) :: t()
1314 | ```
1315 | 
1316 | ```
1317 | @spec ensure_can_submit!(Phoenix.HTML.Form.t()) :: Phoenix.HTML.Form.t()
1318 | ```
1319 | 
1320 | [](AshPhoenix.Form.html#errors/2)
1321 | 
1322 | # errors(form, opts \\\\ \[])
1323 | 
1324 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/form.ex#L2698)
1325 | 
1326 | ```
1327 | @spec errors(t() | Phoenix.HTML.Form.t(), Keyword.t()) ::
1328 |   ([{atom(), {String.t(), Keyword.t()}}]
1329 |    | [String.t()]
1330 |    | [{atom(), String.t()}])
1331 |   | %{
1332 |       required(list()) =>
1333 |         [{atom(), {String.t(), Keyword.t()}}]
1334 |         | [String.t()]
1335 |         | [{atom(), String.t()}]
1336 |     }
1337 | ```
1338 | 
1339 | Returns the errors on the form.
1340 | 
1341 | By default, only errors on the form being passed in (not nested forms) are provided. Use `for_path` to get errors for nested forms.
1342 | 
1343 | - `:format` - Values:
1344 |   
1345 |   ```
1346 |   - `:raw` - `[field:, {message, substitutions}}]` (for translation)
1347 | - `:simple` - `[field: "message w/ variables substituted"]`
1348 | - `:plaintext` - `["field: message w/ variables substituted"]`
1349 |   ```
1350 |   
1351 |   Valid values are :simple, :raw, :plaintext The default value is `:simple`.
1352 | - `:for_path` ([`term/0`](../elixir/typespecs.html#built-in-types)) - 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:  
1353 |   `%{[:comments, 0] => [body: "is invalid"], ...}` The default value is `[]`.
1354 | 
1355 | [](AshPhoenix.Form.html#for_action/3)
1356 | 
1357 | # for\_action(resource\_or\_data, action, opts \\\\ \[])
1358 | 
1359 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/form.ex#L383)
1360 | 
1361 | Creates a form corresponding to any given action on a resource.
1362 | 
1363 | 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.
1364 | 
1365 | ## [](AshPhoenix.Form.html#for_action/3-options)Options
1366 | 
1367 | - `:actor` ([`term/0`](../elixir/typespecs.html#built-in-types)) - The actor performing the action. Passed through to the underlying action.
1368 | - `:forms` ([`keyword/0`](../elixir/typespecs.html#built-in-types)) - Nested form configurations. See [`for_create/3`](AshPhoenix.Form.html#for_create/3) "Nested Form Options" docs for more.
1369 | - `:warn_on_unhandled_errors?` ([`boolean/0`](../elixir/typespecs.html#built-in-types)) - Warns on any errors that don't match the form pattern of `{:field, "message", [replacement: :vars]}` or implement the [`AshPhoenix.FormData.Error`](AshPhoenix.FormData.Error.html) protocol. The default value is `true`.
1370 | - `:domain` ([`atom/0`](../elixir/typespecs.html#basic-types)) - The domain to use when calling the action.
1371 | - `:as` ([`String.t/0`](../elixir/String.html#t: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"`.
1372 | - `:id` ([`String.t/0`](../elixir/String.html#t:t/0)) - The html id of the form. Defaults to the value of `:as` if provided, otherwise "form"
1373 | - `:transform_errors` ([`term/0`](../elixir/typespecs.html#built-in-types)) - Allows for manual manipulation and transformation of errors.  
1374 |   If possible, try to implement [`AshPhoenix.FormData.Error`](AshPhoenix.FormData.Error.html) 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()}]`
1375 | - `: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:
1376 |   
1377 |   - Set default argument values before the validations are run using [`Ash.Changeset.set_arguments/2`](../ash/3.4.55/Ash.Changeset.html#set_arguments/2) or [`Ash.Changeset.set_argument/3`](../ash/3.4.55/Ash.Changeset.html#set_argument/3)
1378 |   - Set changeset context
1379 |   - Do any other pre-processing on the changeset
1380 | - `: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.
1381 | - `: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`](AshPhoenix.Form.html) struct.
1382 | - `:method` ([`String.t/0`](../elixir/String.html#t:t/0)) - The http method to associate with the form. Defaults to `post` for creates, and `put` for everything else.
1383 | - `:exclude_fields_if_empty` - These fields will be ignored if they are empty strings.  
1384 |   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.
1385 | - `:tenant` ([`term/0`](../elixir/typespecs.html#built-in-types)) - The current tenant. Passed through to the underlying action.
1386 | 
1387 | Any *additional* options will be passed to the underlying call to build the source, i.e [`Ash.ActionInput.for_action/4`](../ash/3.4.55/Ash.ActionInput.html#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.
1388 | 
1389 | ## [](AshPhoenix.Form.html#for_action/3-nested-form-options)Nested Form Options
1390 | 
1391 | - `:type` - The cardinality of the nested form - `:list` or `:single`. Valid values are :list, :single The default value is `:single`.
1392 | - `:sparse?` ([`boolean/0`](../elixir/typespecs.html#built-in-types)) - If the nested form is `sparse`, the form won't expect all inputs for all forms to be present.  
1393 |   Has no effect if the type is `:single`.  
1394 |   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}]}`.  
1395 |   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.  
1396 |   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.
1397 | - `:forms` ([`keyword/0`](../elixir/typespecs.html#built-in-types)) - Forms nested inside the current nesting level in all cases.
1398 | - `: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
1399 | - `:merge?` ([`boolean/0`](../elixir/typespecs.html#built-in-types)) - 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`.
1400 | - `:for` ([`atom/0`](../elixir/typespecs.html#basic-types)) - 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`.
1401 | - `:resource` ([`atom/0`](../elixir/typespecs.html#basic-types)) - The resource of the nested forms. Unnecessary if you are providing the `data` key, and not adding additional forms to this path.
1402 | - `:create_action` ([`atom/0`](../elixir/typespecs.html#basic-types)) - The create action to use when building new forms. Only necessary if you want to use [`add_form/3`](AshPhoenix.Form.html#add_form/3) with this path.
1403 | - `:update_action` ([`atom/0`](../elixir/typespecs.html#basic-types)) - The update action to use when building forms for data. Only necessary if you supply the `data` key.
1404 | - `:data` ([`term/0`](../elixir/typespecs.html#built-in-types)) - The current value or values that should have update forms built by default.  
1405 |   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.
1406 | 
1407 | [](AshPhoenix.Form.html#for_create/3)
1408 | 
1409 | # for\_create(resource, action, opts \\\\ \[])
1410 | 
1411 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/form.ex#L533)
1412 | 
1413 | ```
1414 | @spec for_create(Ash.Resource.t(), action :: atom(), opts :: Keyword.t()) :: t()
1415 | ```
1416 | 
1417 | Creates a form corresponding to a create action on a resource.
1418 | 
1419 | ## [](AshPhoenix.Form.html#for_create/3-options)Options
1420 | 
1421 | Options not listed below are passed to the underlying call to build the changeset/query, i.e [`Ash.Changeset.for_create/4`](../ash/3.4.55/Ash.Changeset.html#for_create/4)
1422 | 
1423 | - `:actor` ([`term/0`](../elixir/typespecs.html#built-in-types)) - The actor performing the action. Passed through to the underlying action.
1424 | - `:forms` ([`keyword/0`](../elixir/typespecs.html#built-in-types)) - Nested form configurations. See [`for_create/3`](AshPhoenix.Form.html#for_create/3) "Nested Form Options" docs for more.
1425 | - `:warn_on_unhandled_errors?` ([`boolean/0`](../elixir/typespecs.html#built-in-types)) - Warns on any errors that don't match the form pattern of `{:field, "message", [replacement: :vars]}` or implement the [`AshPhoenix.FormData.Error`](AshPhoenix.FormData.Error.html) protocol. The default value is `true`.
1426 | - `:domain` ([`atom/0`](../elixir/typespecs.html#basic-types)) - The domain to use when calling the action.
1427 | - `:as` ([`String.t/0`](../elixir/String.html#t: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"`.
1428 | - `:id` ([`String.t/0`](../elixir/String.html#t:t/0)) - The html id of the form. Defaults to the value of `:as` if provided, otherwise "form"
1429 | - `:transform_errors` ([`term/0`](../elixir/typespecs.html#built-in-types)) - Allows for manual manipulation and transformation of errors.  
1430 |   If possible, try to implement [`AshPhoenix.FormData.Error`](AshPhoenix.FormData.Error.html) 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()}]`
1431 | - `: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:
1432 |   
1433 |   - Set default argument values before the validations are run using [`Ash.Changeset.set_arguments/2`](../ash/3.4.55/Ash.Changeset.html#set_arguments/2) or [`Ash.Changeset.set_argument/3`](../ash/3.4.55/Ash.Changeset.html#set_argument/3)
1434 |   - Set changeset context
1435 |   - Do any other pre-processing on the changeset
1436 | - `: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.
1437 | - `: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`](AshPhoenix.Form.html) struct.
1438 | - `:method` ([`String.t/0`](../elixir/String.html#t:t/0)) - The http method to associate with the form. Defaults to `post` for creates, and `put` for everything else.
1439 | - `:exclude_fields_if_empty` - These fields will be ignored if they are empty strings.  
1440 |   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.
1441 | - `:tenant` ([`term/0`](../elixir/typespecs.html#built-in-types)) - The current tenant. Passed through to the underlying action.
1442 | 
1443 | ## [](AshPhoenix.Form.html#for_create/3-nested-form-options)Nested Form Options
1444 | 
1445 | [`AshPhoenix.Form`](AshPhoenix.Form.html) automatically determines the nested forms available based on an action's usage of `change manage_relationship(...)`. See the [Related Forms](nested-forms.html) for more.
1446 | 
1447 | - `:type` - The cardinality of the nested form - `:list` or `:single`. Valid values are :list, :single The default value is `:single`.
1448 | - `:sparse?` ([`boolean/0`](../elixir/typespecs.html#built-in-types)) - If the nested form is `sparse`, the form won't expect all inputs for all forms to be present.  
1449 |   Has no effect if the type is `:single`.  
1450 |   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}]}`.  
1451 |   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.  
1452 |   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.
1453 | - `:forms` ([`keyword/0`](../elixir/typespecs.html#built-in-types)) - Forms nested inside the current nesting level in all cases.
1454 | - `: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
1455 | - `:merge?` ([`boolean/0`](../elixir/typespecs.html#built-in-types)) - 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`.
1456 | - `:for` ([`atom/0`](../elixir/typespecs.html#basic-types)) - 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`.
1457 | - `:resource` ([`atom/0`](../elixir/typespecs.html#basic-types)) - The resource of the nested forms. Unnecessary if you are providing the `data` key, and not adding additional forms to this path.
1458 | - `:create_action` ([`atom/0`](../elixir/typespecs.html#basic-types)) - The create action to use when building new forms. Only necessary if you want to use [`add_form/3`](AshPhoenix.Form.html#add_form/3) with this path.
1459 | - `:update_action` ([`atom/0`](../elixir/typespecs.html#basic-types)) - The update action to use when building forms for data. Only necessary if you supply the `data` key.
1460 | - `:data` ([`term/0`](../elixir/typespecs.html#built-in-types)) - The current value or values that should have update forms built by default.  
1461 |   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.
1462 | 
1463 | [](AshPhoenix.Form.html#for_destroy/3)
1464 | 
1465 | # for\_destroy(data, action, opts \\\\ \[])
1466 | 
1467 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/form.ex#L759)
1468 | 
1469 | ```
1470 | @spec for_destroy(Ash.Resource.record(), action :: atom(), opts :: Keyword.t()) :: t()
1471 | ```
1472 | 
1473 | Creates a form corresponding to a destroy action on a record.
1474 | 
1475 | Options:
1476 | 
1477 | - `:actor` ([`term/0`](../elixir/typespecs.html#built-in-types)) - The actor performing the action. Passed through to the underlying action.
1478 | - `:forms` ([`keyword/0`](../elixir/typespecs.html#built-in-types)) - Nested form configurations. See [`for_create/3`](AshPhoenix.Form.html#for_create/3) "Nested Form Options" docs for more.
1479 | - `:warn_on_unhandled_errors?` ([`boolean/0`](../elixir/typespecs.html#built-in-types)) - Warns on any errors that don't match the form pattern of `{:field, "message", [replacement: :vars]}` or implement the [`AshPhoenix.FormData.Error`](AshPhoenix.FormData.Error.html) protocol. The default value is `true`.
1480 | - `:domain` ([`atom/0`](../elixir/typespecs.html#basic-types)) - The domain to use when calling the action.
1481 | - `:as` ([`String.t/0`](../elixir/String.html#t: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"`.
1482 | - `:id` ([`String.t/0`](../elixir/String.html#t:t/0)) - The html id of the form. Defaults to the value of `:as` if provided, otherwise "form"
1483 | - `:transform_errors` ([`term/0`](../elixir/typespecs.html#built-in-types)) - Allows for manual manipulation and transformation of errors.  
1484 |   If possible, try to implement [`AshPhoenix.FormData.Error`](AshPhoenix.FormData.Error.html) 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()}]`
1485 | - `: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:
1486 |   
1487 |   - Set default argument values before the validations are run using [`Ash.Changeset.set_arguments/2`](../ash/3.4.55/Ash.Changeset.html#set_arguments/2) or [`Ash.Changeset.set_argument/3`](../ash/3.4.55/Ash.Changeset.html#set_argument/3)
1488 |   - Set changeset context
1489 |   - Do any other pre-processing on the changeset
1490 | - `: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.
1491 | - `: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`](AshPhoenix.Form.html) struct.
1492 | - `:method` ([`String.t/0`](../elixir/String.html#t:t/0)) - The http method to associate with the form. Defaults to `post` for creates, and `put` for everything else.
1493 | - `:exclude_fields_if_empty` - These fields will be ignored if they are empty strings.  
1494 |   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.
1495 | - `:tenant` ([`term/0`](../elixir/typespecs.html#built-in-types)) - The current tenant. Passed through to the underlying action.
1496 | 
1497 | Any *additional* options will be passed to the underlying call to [`Ash.Changeset.for_destroy/4`](../ash/3.4.55/Ash.Changeset.html#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.
1498 | 
1499 | ## [](AshPhoenix.Form.html#for_destroy/3-nested-form-options)Nested Form Options
1500 | 
1501 | [`AshPhoenix.Form`](AshPhoenix.Form.html) automatically determines the nested forms available based on an action's usage of `change manage_relationship(...)`. See the [Related Forms](nested-forms.html) for more.
1502 | 
1503 | - `:type` - The cardinality of the nested form - `:list` or `:single`. Valid values are :list, :single The default value is `:single`.
1504 | - `:sparse?` ([`boolean/0`](../elixir/typespecs.html#built-in-types)) - If the nested form is `sparse`, the form won't expect all inputs for all forms to be present.  
1505 |   Has no effect if the type is `:single`.  
1506 |   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}]}`.  
1507 |   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.  
1508 |   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.
1509 | - `:forms` ([`keyword/0`](../elixir/typespecs.html#built-in-types)) - Forms nested inside the current nesting level in all cases.
1510 | - `: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
1511 | - `:merge?` ([`boolean/0`](../elixir/typespecs.html#built-in-types)) - 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`.
1512 | - `:for` ([`atom/0`](../elixir/typespecs.html#basic-types)) - 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`.
1513 | - `:resource` ([`atom/0`](../elixir/typespecs.html#basic-types)) - The resource of the nested forms. Unnecessary if you are providing the `data` key, and not adding additional forms to this path.
1514 | - `:create_action` ([`atom/0`](../elixir/typespecs.html#basic-types)) - The create action to use when building new forms. Only necessary if you want to use [`add_form/3`](AshPhoenix.Form.html#add_form/3) with this path.
1515 | - `:update_action` ([`atom/0`](../elixir/typespecs.html#basic-types)) - The update action to use when building forms for data. Only necessary if you supply the `data` key.
1516 | - `:data` ([`term/0`](../elixir/typespecs.html#built-in-types)) - The current value or values that should have update forms built by default.  
1517 |   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.
1518 | 
1519 | [](AshPhoenix.Form.html#for_read/3)
1520 | 
1521 | # for\_read(resource, action, opts \\\\ \[])
1522 | 
1523 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/form.ex#L855)
1524 | 
1525 | ```
1526 | @spec for_read(Ash.Resource.t(), action :: atom(), opts :: Keyword.t()) :: t()
1527 | ```
1528 | 
1529 | Creates a form corresponding to a read action on a resource.
1530 | 
1531 | Options:
1532 | 
1533 | - `:actor` ([`term/0`](../elixir/typespecs.html#built-in-types)) - The actor performing the action. Passed through to the underlying action.
1534 | - `:forms` ([`keyword/0`](../elixir/typespecs.html#built-in-types)) - Nested form configurations. See [`for_create/3`](AshPhoenix.Form.html#for_create/3) "Nested Form Options" docs for more.
1535 | - `:warn_on_unhandled_errors?` ([`boolean/0`](../elixir/typespecs.html#built-in-types)) - Warns on any errors that don't match the form pattern of `{:field, "message", [replacement: :vars]}` or implement the [`AshPhoenix.FormData.Error`](AshPhoenix.FormData.Error.html) protocol. The default value is `true`.
1536 | - `:domain` ([`atom/0`](../elixir/typespecs.html#basic-types)) - The domain to use when calling the action.
1537 | - `:as` ([`String.t/0`](../elixir/String.html#t: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"`.
1538 | - `:id` ([`String.t/0`](../elixir/String.html#t:t/0)) - The html id of the form. Defaults to the value of `:as` if provided, otherwise "form"
1539 | - `:transform_errors` ([`term/0`](../elixir/typespecs.html#built-in-types)) - Allows for manual manipulation and transformation of errors.  
1540 |   If possible, try to implement [`AshPhoenix.FormData.Error`](AshPhoenix.FormData.Error.html) 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()}]`
1541 | - `: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:
1542 |   
1543 |   - Set default argument values before the validations are run using [`Ash.Changeset.set_arguments/2`](../ash/3.4.55/Ash.Changeset.html#set_arguments/2) or [`Ash.Changeset.set_argument/3`](../ash/3.4.55/Ash.Changeset.html#set_argument/3)
1544 |   - Set changeset context
1545 |   - Do any other pre-processing on the changeset
1546 | - `: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.
1547 | - `: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`](AshPhoenix.Form.html) struct.
1548 | - `:method` ([`String.t/0`](../elixir/String.html#t:t/0)) - The http method to associate with the form. Defaults to `post` for creates, and `put` for everything else.
1549 | - `:exclude_fields_if_empty` - These fields will be ignored if they are empty strings.  
1550 |   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.
1551 | - `:tenant` ([`term/0`](../elixir/typespecs.html#built-in-types)) - The current tenant. Passed through to the underlying action.
1552 | 
1553 | Any *additional* options will be passed to the underlying call to [`Ash.Query.for_read/4`](../ash/3.4.55/Ash.Query.html#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.
1554 | 
1555 | 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.
1556 | 
1557 | ## [](AshPhoenix.Form.html#for_read/3-nested-form-options)Nested Form Options
1558 | 
1559 | - `:type` - The cardinality of the nested form - `:list` or `:single`. Valid values are :list, :single The default value is `:single`.
1560 | - `:sparse?` ([`boolean/0`](../elixir/typespecs.html#built-in-types)) - If the nested form is `sparse`, the form won't expect all inputs for all forms to be present.  
1561 |   Has no effect if the type is `:single`.  
1562 |   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}]}`.  
1563 |   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.  
1564 |   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.
1565 | - `:forms` ([`keyword/0`](../elixir/typespecs.html#built-in-types)) - Forms nested inside the current nesting level in all cases.
1566 | - `: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
1567 | - `:merge?` ([`boolean/0`](../elixir/typespecs.html#built-in-types)) - 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`.
1568 | - `:for` ([`atom/0`](../elixir/typespecs.html#basic-types)) - 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`.
1569 | - `:resource` ([`atom/0`](../elixir/typespecs.html#basic-types)) - The resource of the nested forms. Unnecessary if you are providing the `data` key, and not adding additional forms to this path.
1570 | - `:create_action` ([`atom/0`](../elixir/typespecs.html#basic-types)) - The create action to use when building new forms. Only necessary if you want to use [`add_form/3`](AshPhoenix.Form.html#add_form/3) with this path.
1571 | - `:update_action` ([`atom/0`](../elixir/typespecs.html#basic-types)) - The update action to use when building forms for data. Only necessary if you supply the `data` key.
1572 | - `:data` ([`term/0`](../elixir/typespecs.html#built-in-types)) - The current value or values that should have update forms built by default.  
1573 |   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.
1574 | 
1575 | [](AshPhoenix.Form.html#for_update/3)
1576 | 
1577 | # for\_update(data, action, opts \\\\ \[])
1578 | 
1579 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/form.ex#L624)
1580 | 
1581 | ```
1582 | @spec for_update(Ash.Resource.record(), action :: atom(), opts :: Keyword.t()) :: t()
1583 | ```
1584 | 
1585 | Creates a form corresponding to an update action on a record.
1586 | 
1587 | Options:
1588 | 
1589 | - `:actor` ([`term/0`](../elixir/typespecs.html#built-in-types)) - The actor performing the action. Passed through to the underlying action.
1590 | - `:forms` ([`keyword/0`](../elixir/typespecs.html#built-in-types)) - Nested form configurations. See [`for_create/3`](AshPhoenix.Form.html#for_create/3) "Nested Form Options" docs for more.
1591 | - `:warn_on_unhandled_errors?` ([`boolean/0`](../elixir/typespecs.html#built-in-types)) - Warns on any errors that don't match the form pattern of `{:field, "message", [replacement: :vars]}` or implement the [`AshPhoenix.FormData.Error`](AshPhoenix.FormData.Error.html) protocol. The default value is `true`.
1592 | - `:domain` ([`atom/0`](../elixir/typespecs.html#basic-types)) - The domain to use when calling the action.
1593 | - `:as` ([`String.t/0`](../elixir/String.html#t: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"`.
1594 | - `:id` ([`String.t/0`](../elixir/String.html#t:t/0)) - The html id of the form. Defaults to the value of `:as` if provided, otherwise "form"
1595 | - `:transform_errors` ([`term/0`](../elixir/typespecs.html#built-in-types)) - Allows for manual manipulation and transformation of errors.  
1596 |   If possible, try to implement [`AshPhoenix.FormData.Error`](AshPhoenix.FormData.Error.html) 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()}]`
1597 | - `: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:
1598 |   
1599 |   - Set default argument values before the validations are run using [`Ash.Changeset.set_arguments/2`](../ash/3.4.55/Ash.Changeset.html#set_arguments/2) or [`Ash.Changeset.set_argument/3`](../ash/3.4.55/Ash.Changeset.html#set_argument/3)
1600 |   - Set changeset context
1601 |   - Do any other pre-processing on the changeset
1602 | - `: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.
1603 | - `: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`](AshPhoenix.Form.html) struct.
1604 | - `:method` ([`String.t/0`](../elixir/String.html#t:t/0)) - The http method to associate with the form. Defaults to `post` for creates, and `put` for everything else.
1605 | - `:exclude_fields_if_empty` - These fields will be ignored if they are empty strings.  
1606 |   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.
1607 | - `:tenant` ([`term/0`](../elixir/typespecs.html#built-in-types)) - The current tenant. Passed through to the underlying action.
1608 | 
1609 | Any *additional* options will be passed to the underlying call to [`Ash.Changeset.for_update/4`](../ash/3.4.55/Ash.Changeset.html#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.
1610 | 
1611 | ## [](AshPhoenix.Form.html#for_update/3-nested-form-options)Nested Form Options
1612 | 
1613 | [`AshPhoenix.Form`](AshPhoenix.Form.html) automatically determines the nested forms available based on an action's usage of `change manage_relationship(...)`. See the [Related Forms](nested-forms.html) for more.
1614 | 
1615 | - `:type` - The cardinality of the nested form - `:list` or `:single`. Valid values are :list, :single The default value is `:single`.
1616 | - `:sparse?` ([`boolean/0`](../elixir/typespecs.html#built-in-types)) - If the nested form is `sparse`, the form won't expect all inputs for all forms to be present.  
1617 |   Has no effect if the type is `:single`.  
1618 |   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}]}`.  
1619 |   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.  
1620 |   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.
1621 | - `:forms` ([`keyword/0`](../elixir/typespecs.html#built-in-types)) - Forms nested inside the current nesting level in all cases.
1622 | - `: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
1623 | - `:merge?` ([`boolean/0`](../elixir/typespecs.html#built-in-types)) - 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`.
1624 | - `:for` ([`atom/0`](../elixir/typespecs.html#basic-types)) - 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`.
1625 | - `:resource` ([`atom/0`](../elixir/typespecs.html#basic-types)) - The resource of the nested forms. Unnecessary if you are providing the `data` key, and not adding additional forms to this path.
1626 | - `:create_action` ([`atom/0`](../elixir/typespecs.html#basic-types)) - The create action to use when building new forms. Only necessary if you want to use [`add_form/3`](AshPhoenix.Form.html#add_form/3) with this path.
1627 | - `:update_action` ([`atom/0`](../elixir/typespecs.html#basic-types)) - The update action to use when building forms for data. Only necessary if you supply the `data` key.
1628 | - `:data` ([`term/0`](../elixir/typespecs.html#built-in-types)) - The current value or values that should have update forms built by default.  
1629 |   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.
1630 | 
1631 | [](AshPhoenix.Form.html#get_form/2)
1632 | 
1633 | # get\_form(form, path)
1634 | 
1635 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/form.ex#L2577)
1636 | 
1637 | ```
1638 | @spec get_form(t() | Phoenix.HTML.Form.t(), path()) :: t() | nil
1639 | ```
1640 | 
1641 | Gets the form at the specified path
1642 | 
1643 | [](AshPhoenix.Form.html#has_form?/2)
1644 | 
1645 | # has\_form?(form, path)
1646 | 
1647 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/form.ex#L2565)
1648 | 
1649 | ```
1650 | @spec has_form?(t(), path()) :: boolean()
1651 | ```
1652 | 
1653 | Returns true if a given form path exists in the form
1654 | 
1655 | [](AshPhoenix.Form.html#hidden_fields/1)
1656 | 
1657 | # hidden\_fields(form)
1658 | 
1659 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/form.ex#L3668)
1660 | 
1661 | ```
1662 | @spec hidden_fields(t() | Phoenix.HTML.Form.t()) :: Keyword.t()
1663 | ```
1664 | 
1665 | Returns the hidden fields for a form as a keyword list
1666 | 
1667 | [](AshPhoenix.Form.html#ignore/1)
1668 | 
1669 | # ignore(form)
1670 | 
1671 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/form.ex#L3114)
1672 | 
1673 | ```
1674 | @spec ignore(t()) :: t()
1675 | ```
1676 | 
1677 | Toggles the form to be ignored or not ignored.
1678 | 
1679 | 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.
1680 | 
1681 | [](AshPhoenix.Form.html#ignored?/1)
1682 | 
1683 | # ignored?(form)
1684 | 
1685 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/form.ex#L3132)
1686 | 
1687 | ```
1688 | @spec ignored?(t() | Phoenix.HTML.Form.t()) :: boolean()
1689 | ```
1690 | 
1691 | Returns true if the form is ignored
1692 | 
1693 | [](AshPhoenix.Form.html#merge_options/2)
1694 | 
1695 | # merge\_options(form, opts)
1696 | 
1697 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/form.ex#L1333)
1698 | 
1699 | ```
1700 | @spec merge_options(t(), Keyword.t()) :: t()
1701 | ```
1702 | 
1703 | ```
1704 | @spec merge_options(Phoenix.HTML.Form.t(), Keyword.t()) :: Phoenix.HTML.Form.t()
1705 | ```
1706 | 
1707 | Merge the new options with the saved options on a form. See [`update_options/2`](AshPhoenix.Form.html#update_options/2) for more.
1708 | 
1709 | [](AshPhoenix.Form.html#params/2)
1710 | 
1711 | # params(form, opts \\\\ \[])
1712 | 
1713 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/form.ex#L3144)
1714 | 
1715 | Returns the parameters from the form that would be submitted to the action.
1716 | 
1717 | This can be useful if you want to get the parameters and manipulate them/build a custom changeset afterwards.
1718 | 
1719 | [](AshPhoenix.Form.html#parse_path!/3)
1720 | 
1721 | # parse\_path!(form, original\_path, opts \\\\ \[])
1722 | 
1723 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/form.ex#L4540)
1724 | 
1725 | ```
1726 | @spec parse_path!(t() | Phoenix.HTML.Form.t(), path(), opts :: Keyword.t()) ::
1727 |   [atom() | integer()] | no_return()
1728 | ```
1729 | 
1730 | A utility for parsing paths of nested forms in query encoded format.
1731 | 
1732 | For example:
1733 | 
1734 | ```
1735 | parse_path!(form, "post[comments][0][sub_comments][0])
1736 | 
1737 | [:comments, 0, :sub_comments, 0]
1738 | ```
1739 | 
1740 | [](AshPhoenix.Form.html#remove_form/3)
1741 | 
1742 | # remove\_form(form, path, opts \\\\ \[])
1743 | 
1744 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/form.ex#L3509)
1745 | 
1746 | ```
1747 | @spec remove_form(t(), path(), Keyword.t()) :: t()
1748 | ```
1749 | 
1750 | ```
1751 | @spec remove_form(Phoenix.HTML.Form.t(), path(), Keyword.t()) :: Phoenix.HTML.Form.t()
1752 | ```
1753 | 
1754 | Removes a form at the provided path.
1755 | 
1756 | See [`add_form/3`](AshPhoenix.Form.html#add_form/3) for more information on the `path` argument.
1757 | 
1758 | 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:
1759 | 
1760 | ```
1761 | Enum.reduce(removed_form_paths, form, &AshPhoenix.Form.remove_form(&2, &1))
1762 | ```
1763 | 
1764 | - `:validate?` ([`boolean/0`](../elixir/typespecs.html#built-in-types)) - Validates the new full form. The default value is `true`.
1765 | - `:validate_opts` ([`term/0`](../elixir/typespecs.html#built-in-types)) - Options to pass to `validate`. Only used if `validate?` is set to `true` (the default) The default value is `[]`.
1766 | 
1767 | [](AshPhoenix.Form.html#set_data/2)
1768 | 
1769 | # set\_data(form, data)
1770 | 
1771 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/form.ex#L2887)
1772 | 
1773 | Sets the data of the form, in addition to the data of the underlying source, if applicable.
1774 | 
1775 | Queries do not track data (because that wouldn't make sense), so this will not update the data for read actions
1776 | 
1777 | [](AshPhoenix.Form.html#sort_forms/3)
1778 | 
1779 | # sort\_forms(form, path, instruction)
1780 | 
1781 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/form.ex#L2312)
1782 | 
1783 | Sorts nested forms at the given path.
1784 | 
1785 | The following items can be given:
1786 | 
1787 | - `{:replace, path_without_index, [0, 1, 2]}` - indices can be strings or integers.
1788 | - `{:increment, path_to_form}` - increment the index of a specific form (swapping it with the next)
1789 | - `{:decrement, path_to_form}` - decrement the index of a specific form (swapping it with the previous)
1790 | 
1791 | [](AshPhoenix.Form.html#submit/2)
1792 | 
1793 | # submit(form, opts \\\\ \[])
1794 | 
1795 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/form.ex#L1940)
1796 | 
1797 | ```
1798 | @spec submit(t(), Keyword.t()) ::
1799 |   {:ok, Ash.Resource.record() | nil | [Ash.Notifier.Notification.t()]}
1800 |   | {:ok, Ash.Resource.record(), [Ash.Notifier.Notification.t()]}
1801 |   | :ok
1802 |   | {:error, t()}
1803 | ```
1804 | 
1805 | ```
1806 | @spec submit(Phoenix.HTML.Form.t(), Keyword.t()) ::
1807 |   {:ok, Ash.Resource.record() | nil | [Ash.Notifier.Notification.t()]}
1808 |   | {:ok, Ash.Resource.record(), [Ash.Notifier.Notification.t()]}
1809 |   | :ok
1810 |   | {:error, Phoenix.HTML.Form.t()}
1811 | ```
1812 | 
1813 | Submits the form.
1814 | 
1815 | 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.
1816 | 
1817 | Options:
1818 | 
1819 | - `:force?` ([`boolean/0`](../elixir/typespecs.html#built-in-types)) - Submit the form even if it is invalid in its current state. The default value is `false`.
1820 | - `:action_opts` ([`keyword/0`](../elixir/typespecs.html#built-in-types)) - Opts to pass to the call to Ash when calling the action. The default value is `[]`.
1821 | - `:errors` ([`boolean/0`](../elixir/typespecs.html#built-in-types)) - Wether or not to show errors after submitting. The default value is `true`.
1822 | - `:override_params` ([`term/0`](../elixir/typespecs.html#built-in-types)) - If specified, then the params are not extracted from the form.  
1823 |   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`](AshPhoenix.Form.html#params/2). With `override_params`, the form is not validated again, and the `override_params` are passed directly into the action.
1824 | - `:params` ([`term/0`](../elixir/typespecs.html#built-in-types)) - If specified, [`validate/3`](AshPhoenix.Form.html#validate/3) is called with the new params before submitting the form.  
1825 |   This is a shortcut to avoid needing to explicitly validate before every submit.  
1826 |   For example:
1827 |   
1828 |   ```
1829 |   form
1830 | |> AshPhoenix.Form.validate(params)
1831 | |> AshPhoenix.Form.submit()
1832 |   ```
1833 |   
1834 |   Is the same as:
1835 |   
1836 |   ```
1837 |   form
1838 | |> AshPhoenix.Form.submit(params: params)
1839 |   ```
1840 | - `:read_one?` ([`boolean/0`](../elixir/typespecs.html#built-in-types)) - If submitting a read form, a single result will be returned (via read\_one) instead of a list of results.  
1841 |   Ignored for non-read forms. The default value is `false`.
1842 | - `: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.
1843 | 
1844 | [](AshPhoenix.Form.html#submit!/2)
1845 | 
1846 | # submit!(form, opts \\\\ \[])
1847 | 
1848 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/form.ex#L2267)
1849 | 
1850 | ```
1851 | @spec submit!(t(), Keyword.t()) :: Ash.Resource.record() | :ok | no_return()
1852 | ```
1853 | 
1854 | Same as [`submit/2`](AshPhoenix.Form.html#submit/2), but raises an error if the submission fails.
1855 | 
1856 | [](AshPhoenix.Form.html#touch/2)
1857 | 
1858 | # touch(form, fields)
1859 | 
1860 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/form.ex#L2296)
1861 | 
1862 | Mark a field or fields as touched
1863 | 
1864 | To mark nested fields as touched use with [`update_form/4`](AshPhoenix.Form.html#update_form/4) or [`update_forms_at_path/4`](AshPhoenix.Form.html#update_forms_at_path/4)
1865 | 
1866 | [](AshPhoenix.Form.html#update_form/4)
1867 | 
1868 | # update\_form(form, path, func, opts \\\\ \[])
1869 | 
1870 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/form.ex#L2490)
1871 | 
1872 | Updates the form at the provided path using the given function.
1873 | 
1874 | Marks all forms along the path as touched by default. To prevent it, provide `mark_as_touched?: false`.
1875 | 
1876 | This can be useful if you have a button that should modify a nested form in some way, for example.
1877 | 
1878 | [](AshPhoenix.Form.html#update_forms_at_path/4)
1879 | 
1880 | # update\_forms\_at\_path(form, path, func, opts \\\\ \[])
1881 | 
1882 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/form.ex#L2400)
1883 | 
1884 | Updates the list of forms matching a given path. Does not validate that the path points at a single form like [`update_form/4`](AshPhoenix.Form.html#update_form/4).
1885 | 
1886 | 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.
1887 | 
1888 | [](AshPhoenix.Form.html#update_options/2)
1889 | 
1890 | # update\_options(form, fun)
1891 | 
1892 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/form.ex#L1351)
1893 | 
1894 | Update the saved options on a form.
1895 | 
1896 | 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.
1897 | 
1898 | You may want to validate again after this has been changed if it can change the results of your form validation.
1899 | 
1900 | [](AshPhoenix.Form.html#update_params/3)
1901 | 
1902 | # update\_params(form, func, validate\_opts \\\\ \[])
1903 | 
1904 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/form.ex#L1087)
1905 | 
1906 | ```
1907 | @spec update_params(t(), fun :: (map() -> map()), validate_opts :: Keyword.t()) :: t()
1908 | ```
1909 | 
1910 | ```
1911 | @spec update_params(
1912 |   Phoenix.HTML.Form.t(),
1913 |   params :: (map() -> map()),
1914 |   validate_opts :: Keyword.t()
1915 | ) ::
1916 |   Phoenix.HTML.Form.t()
1917 | ```
1918 | 
1919 | Update the previous params provided to the form, and revalidate.
1920 | 
1921 | Accepts the same options as [`validate/2`](AshPhoenix.Form.html#validate/2), passing them through directly.
1922 | 
1923 | You should prefer to use [`validate/2`](AshPhoenix.Form.html#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`](AshPhoenix.Form.html#validate/2).
1924 | 
1925 | 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:
1926 | 
1927 | ```
1928 | <.button phx-click="time-selected" phx-value-time="<%= time %>" />
1929 | ```
1930 | 
1931 | and then have an event handler like this:
1932 | 
1933 | ```
1934 | def handle_event("time-selected", %{"time" => time}, socket) do
1935 |   form = AshPhoenix.Form.update_params(socket.assigns.form, &Map.put(&1, "time", time))
1936 |   {:noreply, assign(socket, :form, form)}
1937 | end
1938 | ```
1939 | 
1940 | [](AshPhoenix.Form.html#validate/3)
1941 | 
1942 | # validate(form, new\_params, opts \\\\ \[])
1943 | 
1944 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/form.ex#L1112)
1945 | 
1946 | ```
1947 | @spec validate(t(), map(), Keyword.t()) :: t()
1948 | ```
1949 | 
1950 | ```
1951 | @spec validate(Phoenix.HTML.Form.t(), map(), Keyword.t()) :: Phoenix.HTML.Form.t()
1952 | ```
1953 | 
1954 | Validates the parameters against the form.
1955 | 
1956 | Options:
1957 | 
1958 | - `:errors` ([`boolean/0`](../elixir/typespecs.html#built-in-types)) - Set to false to hide errors after validation. The default value is `true`.
1959 | - `:target` (list of [`String.t/0`](../elixir/String.html#t:t/0)) - The `_target` param provided by phoenix. Used to support the `only_touched?` option.
1960 | - `:only_touched?` ([`boolean/0`](../elixir/typespecs.html#built-in-types)) - If set to true, only fields that have been marked as touched will be used  
1961 |   If you use this for validation you likely want to use it when submitting as well. The default value is `false`.
1962 | 
1963 | [](AshPhoenix.Form.html#value/2)
1964 | 
1965 | # value(form, field)
1966 | 
1967 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/form.ex#L2969)
1968 | 
1969 | ```
1970 | @spec value(t() | Phoenix.HTML.Form.t(), atom()) :: any()
1971 | ```
1972 | 
1973 | Gets the value for a given field in the form.
1974 | 
1975 | [Hex Package](https://hex.pm/packages/ash_phoenix/2.1.14) [Hex Preview](https://preview.hex.pm/preview/ash_phoenix/2.1.14) Search HexDocs [Download ePub version](ash_phoenix.epub "ePub version")
1976 | 
1977 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.36.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
1978 | [![ash_phoenix](assets/logo.png)](https://github.com/ash-project/ash_phoenix)
1979 | 
1980 | [ash\_phoenix](https://github.com/ash-project/ash_phoenix)
1981 | 
1982 | v2.1.14
1983 | 
1984 | - Pages
1985 | - Modules
1986 | - Mix Tasks
1987 | 
1988 | <!--THE END-->
1989 | 
1990 | <!--THE END-->
1991 | 
1992 | <!--THE END-->
1993 | 
1994 | Search documentation of ash\_phoenix
1995 | 
1996 | Settings
1997 | 
1998 | # [View Source](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/invalid_path.ex#L1 "View Source") AshPhoenix.Form.InvalidPath exception (ash\_phoenix v2.1.14)
1999 | 
2000 | Raised when an invalid path is used to find, update or remove a form
2001 | 
2002 | # [](AshPhoenix.Form.InvalidPath.html#summary)Summary
2003 | 
2004 | ## [Functions](AshPhoenix.Form.InvalidPath.html#functions)
2005 | 
2006 | [message(map)](AshPhoenix.Form.InvalidPath.html#message/1)
2007 | 
2008 | Callback implementation for [`Exception.message/1`](../elixir/Exception.html#c:message/1).
2009 | 
2010 | # [](AshPhoenix.Form.InvalidPath.html#functions)Functions
2011 | 
2012 | [](AshPhoenix.Form.InvalidPath.html#message/1)
2013 | 
2014 | # message(map)
2015 | 
2016 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/invalid_path.ex#L9)
2017 | 
2018 | Callback implementation for [`Exception.message/1`](../elixir/Exception.html#c:message/1).
2019 | 
2020 | [Hex Package](https://hex.pm/packages/ash_phoenix/2.1.14) [Hex Preview](https://preview.hex.pm/preview/ash_phoenix/2.1.14) Search HexDocs [Download ePub version](ash_phoenix.epub "ePub version")
2021 | 
2022 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.36.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
2023 | [![ash_phoenix](assets/logo.png)](https://github.com/ash-project/ash_phoenix)
2024 | 
2025 | [ash\_phoenix](https://github.com/ash-project/ash_phoenix)
2026 | 
2027 | v2.1.14
2028 | 
2029 | - Pages
2030 | - Modules
2031 | - Mix Tasks
2032 | 
2033 | <!--THE END-->
2034 | 
2035 | <!--THE END-->
2036 | 
2037 | <!--THE END-->
2038 | 
2039 | Search documentation of ash\_phoenix
2040 | 
2041 | Settings
2042 | 
2043 | # [View Source](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/no_action_configured.ex#L1 "View Source") AshPhoenix.Form.NoActionConfigured exception (ash\_phoenix v2.1.14)
2044 | 
2045 | Raised when a form action should happen but no action of the appropriate type has been configured
2046 | 
2047 | # [](AshPhoenix.Form.NoActionConfigured.html#summary)Summary
2048 | 
2049 | ## [Functions](AshPhoenix.Form.NoActionConfigured.html#functions)
2050 | 
2051 | [message(map)](AshPhoenix.Form.NoActionConfigured.html#message/1)
2052 | 
2053 | Callback implementation for [`Exception.message/1`](../elixir/Exception.html#c:message/1).
2054 | 
2055 | # [](AshPhoenix.Form.NoActionConfigured.html#functions)Functions
2056 | 
2057 | [](AshPhoenix.Form.NoActionConfigured.html#message/1)
2058 | 
2059 | # message(map)
2060 | 
2061 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/no_action_configured.ex#L9)
2062 | 
2063 | Callback implementation for [`Exception.message/1`](../elixir/Exception.html#c:message/1).
2064 | 
2065 | [Hex Package](https://hex.pm/packages/ash_phoenix/2.1.14) [Hex Preview](https://preview.hex.pm/preview/ash_phoenix/2.1.14) Search HexDocs [Download ePub version](ash_phoenix.epub "ePub version")
2066 | 
2067 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.36.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
2068 | [![ash_phoenix](assets/logo.png)](https://github.com/ash-project/ash_phoenix)
2069 | 
2070 | [ash\_phoenix](https://github.com/ash-project/ash_phoenix)
2071 | 
2072 | v2.1.14
2073 | 
2074 | - Pages
2075 | - Modules
2076 | - Mix Tasks
2077 | 
2078 | <!--THE END-->
2079 | 
2080 | <!--THE END-->
2081 | 
2082 | <!--THE END-->
2083 | 
2084 | Search documentation of ash\_phoenix
2085 | 
2086 | Settings
2087 | 
2088 | # [View Source](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/no_data_loaded.ex#L1 "View Source") AshPhoenix.Form.NoDataLoaded exception (ash\_phoenix v2.1.14)
2089 | 
2090 | Raised when a data needed to be used but the required data was not loaded
2091 | 
2092 | # [](AshPhoenix.Form.NoDataLoaded.html#summary)Summary
2093 | 
2094 | ## [Functions](AshPhoenix.Form.NoDataLoaded.html#functions)
2095 | 
2096 | [message(map)](AshPhoenix.Form.NoDataLoaded.html#message/1)
2097 | 
2098 | Callback implementation for [`Exception.message/1`](../elixir/Exception.html#c:message/1).
2099 | 
2100 | # [](AshPhoenix.Form.NoDataLoaded.html#functions)Functions
2101 | 
2102 | [](AshPhoenix.Form.NoDataLoaded.html#message/1)
2103 | 
2104 | # message(map)
2105 | 
2106 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/no_data_loaded.ex#L9)
2107 | 
2108 | Callback implementation for [`Exception.message/1`](../elixir/Exception.html#c:message/1).
2109 | 
2110 | [Hex Package](https://hex.pm/packages/ash_phoenix/2.1.14) [Hex Preview](https://preview.hex.pm/preview/ash_phoenix/2.1.14) Search HexDocs [Download ePub version](ash_phoenix.epub "ePub version")
2111 | 
2112 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.36.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
2113 | [![ash_phoenix](assets/logo.png)](https://github.com/ash-project/ash_phoenix)
2114 | 
2115 | [ash\_phoenix](https://github.com/ash-project/ash_phoenix)
2116 | 
2117 | v2.1.14
2118 | 
2119 | - Pages
2120 | - Modules
2121 | - Mix Tasks
2122 | 
2123 | <!--THE END-->
2124 | 
2125 | <!--THE END-->
2126 | 
2127 | <!--THE END-->
2128 | 
2129 | Search documentation of ash\_phoenix
2130 | 
2131 | Settings
2132 | 
2133 | # [View Source](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/no_form_configured.ex#L1 "View Source") AshPhoenix.Form.NoFormConfigured exception (ash\_phoenix v2.1.14)
2134 | 
2135 | Raised when attempting to refer to a form but no nested form with that name was configured.
2136 | 
2137 | # [](AshPhoenix.Form.NoFormConfigured.html#summary)Summary
2138 | 
2139 | ## [Functions](AshPhoenix.Form.NoFormConfigured.html#functions)
2140 | 
2141 | [message(error)](AshPhoenix.Form.NoFormConfigured.html#message/1)
2142 | 
2143 | Callback implementation for [`Exception.message/1`](../elixir/Exception.html#c:message/1).
2144 | 
2145 | # [](AshPhoenix.Form.NoFormConfigured.html#functions)Functions
2146 | 
2147 | [](AshPhoenix.Form.NoFormConfigured.html#message/1)
2148 | 
2149 | # message(error)
2150 | 
2151 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/no_form_configured.ex#L15)
2152 | 
2153 | Callback implementation for [`Exception.message/1`](../elixir/Exception.html#c:message/1).
2154 | 
2155 | [Hex Package](https://hex.pm/packages/ash_phoenix/2.1.14) [Hex Preview](https://preview.hex.pm/preview/ash_phoenix/2.1.14) Search HexDocs [Download ePub version](ash_phoenix.epub "ePub version")
2156 | 
2157 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.36.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
2158 | [![ash_phoenix](assets/logo.png)](https://github.com/ash-project/ash_phoenix)
2159 | 
2160 | [ash\_phoenix](https://github.com/ash-project/ash_phoenix)
2161 | 
2162 | v2.1.14
2163 | 
2164 | - Pages
2165 | - Modules
2166 | - Mix Tasks
2167 | 
2168 | <!--THE END-->
2169 | 
2170 | <!--THE END-->
2171 | 
2172 | <!--THE END-->
2173 | 
2174 | Search documentation of ash\_phoenix
2175 | 
2176 | Settings
2177 | 
2178 | # [View Source](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/no_resource_configured.ex#L1 "View Source") AshPhoenix.Form.NoResourceConfigured exception (ash\_phoenix v2.1.14)
2179 | 
2180 | Raised when a form needed to be constructed but the resource for that form could not be determined
2181 | 
2182 | # [](AshPhoenix.Form.NoResourceConfigured.html#summary)Summary
2183 | 
2184 | ## [Functions](AshPhoenix.Form.NoResourceConfigured.html#functions)
2185 | 
2186 | [message(map)](AshPhoenix.Form.NoResourceConfigured.html#message/1)
2187 | 
2188 | Callback implementation for [`Exception.message/1`](../elixir/Exception.html#c:message/1).
2189 | 
2190 | # [](AshPhoenix.Form.NoResourceConfigured.html#functions)Functions
2191 | 
2192 | [](AshPhoenix.Form.NoResourceConfigured.html#message/1)
2193 | 
2194 | # message(map)
2195 | 
2196 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/no_resource_configured.ex#L9)
2197 | 
2198 | Callback implementation for [`Exception.message/1`](../elixir/Exception.html#c:message/1).
2199 | 
2200 | [Hex Package](https://hex.pm/packages/ash_phoenix/2.1.14) [Hex Preview](https://preview.hex.pm/preview/ash_phoenix/2.1.14) Search HexDocs [Download ePub version](ash_phoenix.epub "ePub version")
2201 | 
2202 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.36.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
2203 | [![ash_phoenix](assets/logo.png)](https://github.com/ash-project/ash_phoenix)
2204 | 
2205 | [ash\_phoenix](https://github.com/ash-project/ash_phoenix)
2206 | 
2207 | v2.1.14
2208 | 
2209 | - Pages
2210 | - Modules
2211 | - Mix Tasks
2212 | 
2213 | <!--THE END-->
2214 | 
2215 | <!--THE END-->
2216 | 
2217 | <!--THE END-->
2218 | 
2219 | Search documentation of ash\_phoenix
2220 | 
2221 | Settings
2222 | 
2223 | # [View Source](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/wrapped_value.ex#L1 "View Source") AshPhoenix.Form.WrappedValue (ash\_phoenix v2.1.14)
2224 | 
2225 | A sentinal value used when editing a union that has non-map values
2226 | 
2227 | # [](AshPhoenix.Form.WrappedValue.html#summary)Summary
2228 | 
2229 | ## [Types](AshPhoenix.Form.WrappedValue.html#types)
2230 | 
2231 | [t()](AshPhoenix.Form.WrappedValue.html#t:t/0)
2232 | 
2233 | ## [Functions](AshPhoenix.Form.WrappedValue.html#functions)
2234 | 
2235 | [apply\_constraints\_array(term, constraints)](AshPhoenix.Form.WrappedValue.html#apply_constraints_array/2)
2236 | 
2237 | Callback implementation for [`Ash.Type.apply_constraints_array/2`](../ash/3.4.55/Ash.Type.html#c:apply_constraints_array/2).
2238 | 
2239 | [cast\_input(input, constraints)](AshPhoenix.Form.WrappedValue.html#cast_input/2)
2240 | 
2241 | Callback implementation for [`Ash.Type.cast_input/2`](../ash/3.4.55/Ash.Type.html#c:cast_input/2).
2242 | 
2243 | [cast\_stored(value, constraints)](AshPhoenix.Form.WrappedValue.html#cast_stored/2)
2244 | 
2245 | Callback implementation for [`Ash.Type.cast_stored/2`](../ash/3.4.55/Ash.Type.html#c:cast_stored/2).
2246 | 
2247 | [check\_atomic(value, constraints)](AshPhoenix.Form.WrappedValue.html#check_atomic/2)
2248 | 
2249 | [default\_short\_name()](AshPhoenix.Form.WrappedValue.html#default_short_name/0)
2250 | 
2251 | [dump\_to\_native(value, \_)](AshPhoenix.Form.WrappedValue.html#dump_to_native/2)
2252 | 
2253 | Callback implementation for [`Ash.Type.dump_to_native/2`](../ash/3.4.55/Ash.Type.html#c:dump_to_native/2).
2254 | 
2255 | [fetch\_key(map, atom)](AshPhoenix.Form.WrappedValue.html#fetch_key/2)
2256 | 
2257 | [get\_rewrites(merged\_load, calculation, path, \_)](AshPhoenix.Form.WrappedValue.html#get_rewrites/4)
2258 | 
2259 | Callback implementation for [`Ash.Type.get_rewrites/4`](../ash/3.4.55/Ash.Type.html#c:get_rewrites/4).
2260 | 
2261 | [handle\_change(old\_value, new\_value, constraints)](AshPhoenix.Form.WrappedValue.html#handle_change/3)
2262 | 
2263 | Callback implementation for [`Ash.Type.handle_change/3`](../ash/3.4.55/Ash.Type.html#c:handle_change/3).
2264 | 
2265 | [handle\_change?()](AshPhoenix.Form.WrappedValue.html#handle_change?/0)
2266 | 
2267 | [handle\_change\_array(old\_values, new\_values, constraints)](AshPhoenix.Form.WrappedValue.html#handle_change_array/3)
2268 | 
2269 | Callback implementation for [`Ash.Type.handle_change_array/3`](../ash/3.4.55/Ash.Type.html#c:handle_change_array/3).
2270 | 
2271 | [input(opts)](AshPhoenix.Form.WrappedValue.html#input/1)
2272 | 
2273 | Validates that the keys in the provided input are valid for at least one action on the resource.
2274 | 
2275 | [input(opts, action)](AshPhoenix.Form.WrappedValue.html#input/2)
2276 | 
2277 | Same as [`input/1`](AshPhoenix.Form.WrappedValue.html#input/1), except restricts the keys to values accepted by the action provided.
2278 | 
2279 | [load(record, load, constraints, context)](AshPhoenix.Form.WrappedValue.html#load/4)
2280 | 
2281 | Callback implementation for [`Ash.Type.load/4`](../ash/3.4.55/Ash.Type.html#c:load/4).
2282 | 
2283 | [prepare\_change(old\_value, new\_value, constraints)](AshPhoenix.Form.WrappedValue.html#prepare_change/3)
2284 | 
2285 | Callback implementation for [`Ash.Type.prepare_change/3`](../ash/3.4.55/Ash.Type.html#c:prepare_change/3).
2286 | 
2287 | [prepare\_change?()](AshPhoenix.Form.WrappedValue.html#prepare_change?/0)
2288 | 
2289 | [prepare\_change\_array(old\_values, new\_uncasted\_values, constraints)](AshPhoenix.Form.WrappedValue.html#prepare_change_array/3)
2290 | 
2291 | Callback implementation for [`Ash.Type.prepare_change_array/3`](../ash/3.4.55/Ash.Type.html#c:prepare_change_array/3).
2292 | 
2293 | [rewrite(value, rewrites, constraints)](AshPhoenix.Form.WrappedValue.html#rewrite/3)
2294 | 
2295 | Callback implementation for [`Ash.Type.rewrite/3`](../ash/3.4.55/Ash.Type.html#c:rewrite/3).
2296 | 
2297 | [storage\_type(\_)](AshPhoenix.Form.WrappedValue.html#storage_type/1)
2298 | 
2299 | Callback implementation for [`Ash.Type.storage_type/1`](../ash/3.4.55/Ash.Type.html#c:storage_type/1).
2300 | 
2301 | # [](AshPhoenix.Form.WrappedValue.html#types)Types
2302 | 
2303 | [](AshPhoenix.Form.WrappedValue.html#t:t/0)
2304 | 
2305 | # t()
2306 | 
2307 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/wrapped_value.ex#L1)
2308 | 
2309 | ```
2310 | @type t() :: %AshPhoenix.Form.WrappedValue{
2311 |   __lateral_join_source__: term(),
2312 |   __meta__: term(),
2313 |   __metadata__: term(),
2314 |   __order__: term(),
2315 |   aggregates: term(),
2316 |   calculations: term(),
2317 |   value: term()
2318 | }
2319 | ```
2320 | 
2321 | # [](AshPhoenix.Form.WrappedValue.html#functions)Functions
2322 | 
2323 | [](AshPhoenix.Form.WrappedValue.html#apply_constraints_array/2)
2324 | 
2325 | # apply\_constraints\_array(term, constraints)
2326 | 
2327 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/wrapped_value.ex#L3)
2328 | 
2329 | Callback implementation for [`Ash.Type.apply_constraints_array/2`](../ash/3.4.55/Ash.Type.html#c:apply_constraints_array/2).
2330 | 
2331 | [](AshPhoenix.Form.WrappedValue.html#cast_input/2)
2332 | 
2333 | # cast\_input(input, constraints)
2334 | 
2335 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/wrapped_value.ex#L3)
2336 | 
2337 | Callback implementation for [`Ash.Type.cast_input/2`](../ash/3.4.55/Ash.Type.html#c:cast_input/2).
2338 | 
2339 | [](AshPhoenix.Form.WrappedValue.html#cast_stored/2)
2340 | 
2341 | # cast\_stored(value, constraints)
2342 | 
2343 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/wrapped_value.ex#L3)
2344 | 
2345 | Callback implementation for [`Ash.Type.cast_stored/2`](../ash/3.4.55/Ash.Type.html#c:cast_stored/2).
2346 | 
2347 | [](AshPhoenix.Form.WrappedValue.html#check_atomic/2)
2348 | 
2349 | # check\_atomic(value, constraints)
2350 | 
2351 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/wrapped_value.ex#L3)
2352 | 
2353 | [](AshPhoenix.Form.WrappedValue.html#default_short_name/0)
2354 | 
2355 | # default\_short\_name()
2356 | 
2357 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/wrapped_value.ex#L1)
2358 | 
2359 | [](AshPhoenix.Form.WrappedValue.html#dump_to_native/2)
2360 | 
2361 | # dump\_to\_native(value, \_)
2362 | 
2363 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/wrapped_value.ex#L3)
2364 | 
2365 | Callback implementation for [`Ash.Type.dump_to_native/2`](../ash/3.4.55/Ash.Type.html#c:dump_to_native/2).
2366 | 
2367 | [](AshPhoenix.Form.WrappedValue.html#fetch_key/2)
2368 | 
2369 | # fetch\_key(map, atom)
2370 | 
2371 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/wrapped_value.ex#L3)
2372 | 
2373 | [](AshPhoenix.Form.WrappedValue.html#get_rewrites/4)
2374 | 
2375 | # get\_rewrites(merged\_load, calculation, path, \_)
2376 | 
2377 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/wrapped_value.ex#L3)
2378 | 
2379 | Callback implementation for [`Ash.Type.get_rewrites/4`](../ash/3.4.55/Ash.Type.html#c:get_rewrites/4).
2380 | 
2381 | [](AshPhoenix.Form.WrappedValue.html#handle_change/3)
2382 | 
2383 | # handle\_change(old\_value, new\_value, constraints)
2384 | 
2385 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/wrapped_value.ex#L3)
2386 | 
2387 | Callback implementation for [`Ash.Type.handle_change/3`](../ash/3.4.55/Ash.Type.html#c:handle_change/3).
2388 | 
2389 | [](AshPhoenix.Form.WrappedValue.html#handle_change?/0)
2390 | 
2391 | # handle\_change?()
2392 | 
2393 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/wrapped_value.ex#L1)
2394 | 
2395 | [](AshPhoenix.Form.WrappedValue.html#handle_change_array/3)
2396 | 
2397 | # handle\_change\_array(old\_values, new\_values, constraints)
2398 | 
2399 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/wrapped_value.ex#L3)
2400 | 
2401 | Callback implementation for [`Ash.Type.handle_change_array/3`](../ash/3.4.55/Ash.Type.html#c:handle_change_array/3).
2402 | 
2403 | [](AshPhoenix.Form.WrappedValue.html#input/1)
2404 | 
2405 | # input(opts)
2406 | 
2407 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/wrapped_value.ex#L1)
2408 | 
2409 | ```
2410 | @spec input(values :: map() | Keyword.t()) :: map() | no_return()
2411 | ```
2412 | 
2413 | Validates that the keys in the provided input are valid for at least one action on the resource.
2414 | 
2415 | Raises a KeyError error at compile time if not. This exists because generally a struct should only ever be created by Ash as a result of a successful action. You should not be creating records manually in code, e.g `%MyResource{value: 1, value: 2}`. Generally that is fine, but often with embedded resources it is nice to be able to validate the keys that are being provided, e.g
2416 | 
2417 | ```
2418 | Resource
2419 | |> Ash.Changeset.for_create(:create, %{embedded: EmbeddedResource.input(foo: 1, bar: 2)})
2420 | |> Ash.create()
2421 | ```
2422 | 
2423 | [](AshPhoenix.Form.WrappedValue.html#input/2)
2424 | 
2425 | # input(opts, action)
2426 | 
2427 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/wrapped_value.ex#L1)
2428 | 
2429 | ```
2430 | @spec input(values :: map() | Keyword.t(), action :: atom()) :: map() | no_return()
2431 | ```
2432 | 
2433 | Same as [`input/1`](AshPhoenix.Form.WrappedValue.html#input/1), except restricts the keys to values accepted by the action provided.
2434 | 
2435 | [](AshPhoenix.Form.WrappedValue.html#load/4)
2436 | 
2437 | # load(record, load, constraints, context)
2438 | 
2439 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/wrapped_value.ex#L3)
2440 | 
2441 | Callback implementation for [`Ash.Type.load/4`](../ash/3.4.55/Ash.Type.html#c:load/4).
2442 | 
2443 | [](AshPhoenix.Form.WrappedValue.html#prepare_change/3)
2444 | 
2445 | # prepare\_change(old\_value, new\_value, constraints)
2446 | 
2447 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/wrapped_value.ex#L3)
2448 | 
2449 | Callback implementation for [`Ash.Type.prepare_change/3`](../ash/3.4.55/Ash.Type.html#c:prepare_change/3).
2450 | 
2451 | [](AshPhoenix.Form.WrappedValue.html#prepare_change?/0)
2452 | 
2453 | # prepare\_change?()
2454 | 
2455 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/wrapped_value.ex#L1)
2456 | 
2457 | [](AshPhoenix.Form.WrappedValue.html#prepare_change_array/3)
2458 | 
2459 | # prepare\_change\_array(old\_values, new\_uncasted\_values, constraints)
2460 | 
2461 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/wrapped_value.ex#L3)
2462 | 
2463 | Callback implementation for [`Ash.Type.prepare_change_array/3`](../ash/3.4.55/Ash.Type.html#c:prepare_change_array/3).
2464 | 
2465 | [](AshPhoenix.Form.WrappedValue.html#rewrite/3)
2466 | 
2467 | # rewrite(value, rewrites, constraints)
2468 | 
2469 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/wrapped_value.ex#L3)
2470 | 
2471 | Callback implementation for [`Ash.Type.rewrite/3`](../ash/3.4.55/Ash.Type.html#c:rewrite/3).
2472 | 
2473 | [](AshPhoenix.Form.WrappedValue.html#storage_type/1)
2474 | 
2475 | # storage\_type(\_)
2476 | 
2477 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/form/wrapped_value.ex#L3)
2478 | 
2479 | Callback implementation for [`Ash.Type.storage_type/1`](../ash/3.4.55/Ash.Type.html#c:storage_type/1).
2480 | 
2481 | [Hex Package](https://hex.pm/packages/ash_phoenix/2.1.14) [Hex Preview](https://preview.hex.pm/preview/ash_phoenix/2.1.14) Search HexDocs [Download ePub version](ash_phoenix.epub "ePub version")
2482 | 
2483 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.36.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
2484 | [![ash_phoenix](assets/logo.png)](https://github.com/ash-project/ash_phoenix)
2485 | 
2486 | [ash\_phoenix](https://github.com/ash-project/ash_phoenix)
2487 | 
2488 | v2.1.14
2489 | 
2490 | - Pages
2491 | - Modules
2492 | - Mix Tasks
2493 | 
2494 | <!--THE END-->
2495 | 
2496 | <!--THE END-->
2497 | 
2498 | <!--THE END-->
2499 | 
2500 | Search documentation of ash\_phoenix
2501 | 
2502 | Settings
2503 | 
2504 | # [View Source](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix.ex#L1 "View Source") AshPhoenix (ash\_phoenix v2.1.14)
2505 | 
2506 | An extension to add form builders to the code interface.
2507 | 
2508 | There is currently no DSL for this extension.
2509 | 
2510 | This defines a `form_to_<name>` function for each code interface function. Positional arguments are ignored, given that in forms, all input typically comes from the `params` map.
2511 | 
2512 | The generated function passes all options through to [`AshPhoenix.Form.for_action/3`](AshPhoenix.Form.html#for_action/3)
2513 | 
2514 | Update and destroy actions take the record being updated/destroyed as the first argument.
2515 | 
2516 | For example, given this code interface definition on a domain called `MyApp.Accounts`:
2517 | 
2518 | ```
2519 | resources do
2520 |   resource MyApp.Accounts.User do
2521 |     define :register_with_password, args: [:email, :password]
2522 |     define :update_user, action: :update, args: [:email, :password]
2523 |   end
2524 | end
2525 | ```
2526 | 
2527 | Adding the [`AshPhoenix`](AshPhoenix.html) extension would define `form_to_register_with_password/2`.
2528 | 
2529 | ## [](AshPhoenix.html#module-usage)Usage
2530 | 
2531 | Without options:
2532 | 
2533 | ```
2534 | MyApp.Accounts.form_to_register_with_password()
2535 | #=> %AshPhoenix.Form{}
2536 | ```
2537 | 
2538 | With options:
2539 | 
2540 | ```
2541 | MyApp.Accounts.form_to_register_with_password(params: %{"email" => "placeholder@email"})
2542 | #=> %AshPhoenix.Form{}
2543 | ```
2544 | 
2545 | With
2546 | 
2547 | ```
2548 | MyApp.Accounts.form_to_update_user(params: %{"email" => "placeholder@email"})
2549 | #=> %AshPhoenix.Form{}
2550 | ```
2551 | 
2552 | [Hex Package](https://hex.pm/packages/ash_phoenix/2.1.14) [Hex Preview](https://preview.hex.pm/preview/ash_phoenix/2.1.14) Search HexDocs [Download ePub version](ash_phoenix.epub "ePub version")
2553 | 
2554 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.36.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
2555 | [![ash_phoenix](assets/logo.png)](https://github.com/ash-project/ash_phoenix)
2556 | 
2557 | [ash\_phoenix](https://github.com/ash-project/ash_phoenix)
2558 | 
2559 | v2.1.14
2560 | 
2561 | - Pages
2562 | - Modules
2563 | - Mix Tasks
2564 | 
2565 | <!--THE END-->
2566 | 
2567 | <!--THE END-->
2568 | 
2569 | <!--THE END-->
2570 | 
2571 | Search documentation of ash\_phoenix
2572 | 
2573 | Settings
2574 | 
2575 | # [View Source](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/live_view.ex#L1 "View Source") AshPhoenix.LiveView (ash\_phoenix v2.1.14)
2576 | 
2577 | Utilities for keeping Ash query results up to date in a LiveView.
2578 | 
2579 | # [](AshPhoenix.LiveView.html#summary)Summary
2580 | 
2581 | ## [Types](AshPhoenix.LiveView.html#types)
2582 | 
2583 | [assign()](AshPhoenix.LiveView.html#t:assign/0)
2584 | 
2585 | [assigns()](AshPhoenix.LiveView.html#t:assigns/0)
2586 | 
2587 | [callback()](AshPhoenix.LiveView.html#t:callback/0)
2588 | 
2589 | [callback\_result()](AshPhoenix.LiveView.html#t:callback_result/0)
2590 | 
2591 | [liveness\_options()](AshPhoenix.LiveView.html#t:liveness_options/0)
2592 | 
2593 | [socket()](AshPhoenix.LiveView.html#t:socket/0)
2594 | 
2595 | [topic()](AshPhoenix.LiveView.html#t:topic/0)
2596 | 
2597 | ## [Functions](AshPhoenix.LiveView.html#functions)
2598 | 
2599 | [can\_link\_to\_page?(page, target)](AshPhoenix.LiveView.html#can_link_to_page?/2)
2600 | 
2601 | [change\_page(socket, assign, target)](AshPhoenix.LiveView.html#change_page/3)
2602 | 
2603 | [handle\_live(socket, notification, assigns, refetch\_info \\\\ \[\])](AshPhoenix.LiveView.html#handle_live/4)
2604 | 
2605 | Incorporates an [`Ash.Notifier.Notification`](../ash/3.4.55/Ash.Notifier.Notification.html) into the query results, based on the liveness configuration.
2606 | 
2607 | [keep\_live(socket, assign, callback, opts \\\\ \[\])](AshPhoenix.LiveView.html#keep_live/4)
2608 | 
2609 | Runs the callback, and stores the information required to keep it live in the socket assigns.
2610 | 
2611 | [last\_page(arg1)](AshPhoenix.LiveView.html#last_page/1)
2612 | 
2613 | [next\_page?(page)](AshPhoenix.LiveView.html#next_page?/1)
2614 | 
2615 | [on\_page?(page, num)](AshPhoenix.LiveView.html#on_page?/2)
2616 | 
2617 | [page\_from\_params(params, default\_limit, count? \\\\ false)](AshPhoenix.LiveView.html#page_from_params/3)
2618 | 
2619 | [page\_link\_params(offset, target)](AshPhoenix.LiveView.html#page_link_params/2)
2620 | 
2621 | [page\_number(arg1)](AshPhoenix.LiveView.html#page_number/1)
2622 | 
2623 | [page\_params(keyset)](AshPhoenix.LiveView.html#page_params/1)
2624 | 
2625 | [prev\_page?(page)](AshPhoenix.LiveView.html#prev_page?/1)
2626 | 
2627 | # [](AshPhoenix.LiveView.html#types)Types
2628 | 
2629 | [](AshPhoenix.LiveView.html#t:assign/0)
2630 | 
2631 | # assign()
2632 | 
2633 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/live_view.ex#L7)
2634 | 
2635 | ```
2636 | @type assign() :: atom()
2637 | ```
2638 | 
2639 | [](AshPhoenix.LiveView.html#t:assigns/0)
2640 | 
2641 | # assigns()
2642 | 
2643 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/live_view.ex#L8)
2644 | 
2645 | ```
2646 | @type assigns() :: map()
2647 | ```
2648 | 
2649 | [](AshPhoenix.LiveView.html#t:callback/0)
2650 | 
2651 | # callback()
2652 | 
2653 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/live_view.ex#L131)
2654 | 
2655 | ```
2656 | @type callback() ::
2657 |   (socket() -> callback_result())
2658 |   | (socket(), Keyword.t() | nil -> callback_result())
2659 | ```
2660 | 
2661 | [](AshPhoenix.LiveView.html#t:callback_result/0)
2662 | 
2663 | # callback\_result()
2664 | 
2665 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/live_view.ex#L130)
2666 | 
2667 | ```
2668 | @type callback_result() :: struct() | [struct()] | Ash.Page.page() | nil
2669 | ```
2670 | 
2671 | [](AshPhoenix.LiveView.html#t:liveness_options/0)
2672 | 
2673 | # liveness\_options()
2674 | 
2675 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/live_view.ex#L10)
2676 | 
2677 | ```
2678 | @type liveness_options() :: Keyword.t()
2679 | ```
2680 | 
2681 | [](AshPhoenix.LiveView.html#t:socket/0)
2682 | 
2683 | # socket()
2684 | 
2685 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/live_view.ex#L6)
2686 | 
2687 | ```
2688 | @type socket() :: term()
2689 | ```
2690 | 
2691 | [](AshPhoenix.LiveView.html#t:topic/0)
2692 | 
2693 | # topic()
2694 | 
2695 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/live_view.ex#L9)
2696 | 
2697 | ```
2698 | @type topic() :: String.t()
2699 | ```
2700 | 
2701 | # [](AshPhoenix.LiveView.html#functions)Functions
2702 | 
2703 | [](AshPhoenix.LiveView.html#can_link_to_page?/2)
2704 | 
2705 | # can\_link\_to\_page?(page, target)
2706 | 
2707 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/live_view.ex#L352)
2708 | 
2709 | [](AshPhoenix.LiveView.html#change_page/3)
2710 | 
2711 | # change\_page(socket, assign, target)
2712 | 
2713 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/live_view.ex#L194)
2714 | 
2715 | [](AshPhoenix.LiveView.html#handle_live/4)
2716 | 
2717 | # handle\_live(socket, notification, assigns, refetch\_info \\\\ \[])
2718 | 
2719 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/live_view.ex#L402)
2720 | 
2721 | Incorporates an [`Ash.Notifier.Notification`](../ash/3.4.55/Ash.Notifier.Notification.html) into the query results, based on the liveness configuration.
2722 | 
2723 | You will want to match on receiving a notification from Ash, and the easiest way to do that is to match on the payload like so:
2724 | 
2725 | ```
2726 |   @impl true
2727 | def handle_info(%{topic: topic, payload: %Ash.Notifier.Notification{}}, socket) do
2728 |   {:noreply, handle_live(socket, topic, [:query1, :query2, :query3])}
2729 | end
2730 | ```
2731 | 
2732 | Feel free to intercept notifications and do your own logic to respond to events. Ultimately, all that matters is that you also call [`handle_live/3`](AshPhoenix.LiveView.html#handle_live/3) if you want it to update your query results.
2733 | 
2734 | The assign or list of assigns passed as the third argument must be the same names passed into `keep_live`. If you only want some queries to update based on some events, you can define multiple matches on events, and only call [`handle_live/3`](AshPhoenix.LiveView.html#handle_live/3) with the assigns that should be updated for that notification.
2735 | 
2736 | [](AshPhoenix.LiveView.html#keep_live/4)
2737 | 
2738 | # keep\_live(socket, assign, callback, opts \\\\ \[])
2739 | 
2740 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/live_view.ex#L134)
2741 | 
2742 | ```
2743 | @spec keep_live(socket(), assign(), callback(), liveness_options()) :: socket()
2744 | ```
2745 | 
2746 | Runs the callback, and stores the information required to keep it live in the socket assigns.
2747 | 
2748 | The data will be assigned to the provided key, e.g `keep_live(socket, :me, ...)` would assign the results to `:me` (accessed as `@me` in the template).
2749 | 
2750 | Additionally, you'll need to define a `handle_info/2` callback for your liveview to receive any notifications, and pass that notification into [`handle_live/3`](AshPhoenix.LiveView.html#handle_live/3). See [`handle_live/3`](AshPhoenix.LiveView.html#handle_live/3) for more.
2751 | 
2752 | ## [](AshPhoenix.LiveView.html#keep_live/4-important)Important
2753 | 
2754 | The logic for handling events to keep data live is currently very limited. It will simply rerun the query every time. To this end, you should feel free to intercept individual events and handle them yourself for more optimized liveness.
2755 | 
2756 | ## [](AshPhoenix.LiveView.html#keep_live/4-pagination)Pagination
2757 | 
2758 | To make paginated views convenient, as well as making it possible to keep those views live, Ash does not simply rerun the query when it gets an update, as that could involve shuffling the records around on the page. Eventually this will be configurable, but for now, Ash simply adjusts the query to only include the records that are on the page. If a record would be removed from a page due to a data change, it will simply be left there. For the best performance, use `keyset` pagination. If you *need* the ability to jump to a page by number, you'll want to use `offset` pagination, but keep in mind that it performs worse on large tables.
2759 | 
2760 | To support this, accept a second parameter to your callback function, which will be the options to use in `page_opts`
2761 | 
2762 | ## [](AshPhoenix.LiveView.html#keep_live/4-options)Options:
2763 | 
2764 | - `:subscribe` - A topic or list of topics that should cause this data to update.
2765 | - `:refetch?` ([`boolean/0`](../elixir/typespecs.html#built-in-types)) - A boolean flag indicating whether a refetch is allowed to happen. Defaults to `true`
2766 | - `:after_fetch` ([`term/0`](../elixir/typespecs.html#built-in-types)) - A two argument function that takes the results, and the socket, and returns the new socket. Can be used to set assigns based on the result of the query.
2767 | - `:results` - For list and page queries, by default the records shown are never changed (unless the page changes) Valid values are :keep, :lose The default value is `:keep`.
2768 | - `:load_until_connected?` ([`boolean/0`](../elixir/typespecs.html#built-in-types)) - If the socket is not connected, then the value of the provided assign is set to `:loading`. Has no effect if `initial` is provided.
2769 | - `:initial` ([`term/0`](../elixir/typespecs.html#built-in-types)) - Results to use instead of running the query immediately.
2770 | - `:refetch_interval` ([`non_neg_integer/0`](../elixir/typespecs.html#basic-types)) - An interval (in ms) to periodically refetch the query
2771 | - `:refetch_window` ([`non_neg_integer/0`](../elixir/typespecs.html#basic-types)) - The minimum time (in ms) between refetches, including refetches caused by notifications.
2772 | 
2773 | A great way to get readable millisecond values is to use the functions in erlang's `:timer` module, like [`:timer.hours/1`](https://www.erlang.org/doc/apps/stdlib/timer.html#hours/1), [`:timer.minutes/1`](https://www.erlang.org/doc/apps/stdlib/timer.html#minutes/1), and [`:timer.seconds/1`](https://www.erlang.org/doc/apps/stdlib/timer.html#seconds/1)
2774 | 
2775 | #### refetch\_interval
2776 | 
2777 | If this option is set, a message is sent as `{:refetch, assign_name, opts}` on that interval. You can then match on that event, like so:
2778 | 
2779 | ```
2780 | def handle_info({:refetch, assign, opts}, socket) do
2781 |   {:noreply, handle_live(socket, :refetch, assign, opts)}
2782 | end
2783 | ```
2784 | 
2785 | This is the equivalent of `:timer.send_interval(interval, {:refetch, assign, opts})`, so feel free to roll your own solution if you have complex refetching requirements.
2786 | 
2787 | #### refetch\_window
2788 | 
2789 | Normally, when a pubsub message is received the query is rerun. This option will cause the query to wait at least this amount of time before doing a refetch. This is accomplished with [`Process.send_after/4`](../elixir/Process.html#send_after/4), and recording the last time each query was refetched. For example if a refetch happens at time `0`, and the `refetch_window` is 10,000 ms, we would refetch, and record the time. Then if another refetch should happen 5,000 ms later, we would look and see that we need to wait another 5,000ms. So we use [`Process.send_after/4`](../elixir/Process.html#send_after/4) to send a `{:refetch, assign, opts}` message in 5,000ms. The time that a refetch was requested is tracked, so if the data has since been refetched, it won't be refetched again.
2790 | 
2791 | #### Future Plans
2792 | 
2793 | One interesting thing here is that, given that we know the scope of data that a resource cares about, we should be able to make optimizations to this code, to support partial refetches, or even just updating the data directly. However, this will need to be carefully considered, as the risks involve showing users data they could be unauthorized to see, or having state in the socket that is inconsistent.
2794 | 
2795 | [](AshPhoenix.LiveView.html#last_page/1)
2796 | 
2797 | # last\_page(arg1)
2798 | 
2799 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/live_view.ex#L356)
2800 | 
2801 | [](AshPhoenix.LiveView.html#next_page?/1)
2802 | 
2803 | # next\_page?(page)
2804 | 
2805 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/live_view.ex#L281)
2806 | 
2807 | [](AshPhoenix.LiveView.html#on_page?/2)
2808 | 
2809 | # on\_page?(page, num)
2810 | 
2811 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/live_view.ex#L366)
2812 | 
2813 | [](AshPhoenix.LiveView.html#page_from_params/3)
2814 | 
2815 | # page\_from\_params(params, default\_limit, count? \\\\ false)
2816 | 
2817 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/live_view.ex#L228)
2818 | 
2819 | [](AshPhoenix.LiveView.html#page_link_params/2)
2820 | 
2821 | # page\_link\_params(offset, target)
2822 | 
2823 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/live_view.ex#L285)
2824 | 
2825 | [](AshPhoenix.LiveView.html#page_number/1)
2826 | 
2827 | # page\_number(arg1)
2828 | 
2829 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/live_view.ex#L370)
2830 | 
2831 | [](AshPhoenix.LiveView.html#page_params/1)
2832 | 
2833 | # page\_params(keyset)
2834 | 
2835 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/live_view.ex#L248)
2836 | 
2837 | [](AshPhoenix.LiveView.html#prev_page?/1)
2838 | 
2839 | # prev\_page?(page)
2840 | 
2841 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/live_view.ex#L277)
2842 | 
2843 | [Hex Package](https://hex.pm/packages/ash_phoenix/2.1.14) [Hex Preview](https://preview.hex.pm/preview/ash_phoenix/2.1.14) Search HexDocs [Download ePub version](ash_phoenix.epub "ePub version")
2844 | 
2845 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.36.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
2846 | [![ash_phoenix](assets/logo.png)](https://github.com/ash-project/ash_phoenix)
2847 | 
2848 | [ash\_phoenix](https://github.com/ash-project/ash_phoenix)
2849 | 
2850 | v2.1.14
2851 | 
2852 | - Pages
2853 | - Modules
2854 | - Mix Tasks
2855 | 
2856 | <!--THE END-->
2857 | 
2858 | <!--THE END-->
2859 | 
2860 | <!--THE END-->
2861 | 
2862 | Search documentation of ash\_phoenix
2863 | 
2864 | Settings
2865 | 
2866 | # [View Source](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/plug/subdomain_plug.ex#L1 "View Source") AshPhoenix.SubdomainPlug (ash\_phoenix v2.1.14)
2867 | 
2868 | This is a basic plug that loads the current tenant assign from a given value set on subdomain.
2869 | 
2870 | This was copied from `Triplex.SubdomainPlug`, here: [https://github.com/ateliware/triplex/blob/master/lib/triplex/plugs/subdomain\_plug.ex](https://github.com/ateliware/triplex/blob/master/lib/triplex/plugs/subdomain_plug.ex)
2871 | 
2872 | Options:
2873 | 
2874 | - `:endpoint` ([`atom/0`](../elixir/typespecs.html#basic-types)) - Required. The endpoint that the plug is in, used for deterining the host
2875 | - `:assign` ([`atom/0`](../elixir/typespecs.html#basic-types)) - The key to use when assigning the current tenant The default value is `:current_tenant`.
2876 | - `:handle_subdomain` - An mfa to call with the conn and a subdomain value. Can be used to do something like fetch the current user given the tenant. Must return the new conn.
2877 | 
2878 | To plug it on your router, you can use:
2879 | 
2880 | ```
2881 | plug AshPhoenix.SubdomainPlug,
2882 |   endpoint: MyApp.Endpoint
2883 | ```
2884 | 
2885 | An additional helper here can be used for determining the host in your liveview, and/or using the host that was already assigned to the conn.
2886 | 
2887 | For example:
2888 | 
2889 | ```
2890 | def handle_params(params, uri, socket) do
2891 |   socket =
2892 |     assign_new(socket, :current_tenant, fn ->
2893 |       AshPhoenix.SubdomainPlug.live_tenant(socket, uri)
2894 |     end)
2895 | 
2896 |   socket =
2897 |     assign_new(socket, :current_organization, fn ->
2898 |       if socket.assigns[:current_tenant] do
2899 |         MyApp.Accounts.Ash.get!(MyApp.Accounts.Organization,
2900 |           subdomain: socket.assigns[:current_tenant]
2901 |         )
2902 |       end
2903 |     end)
2904 | 
2905 |   {:noreply, socket}
2906 | end
2907 | ```
2908 | 
2909 | # [](AshPhoenix.SubdomainPlug.html#summary)Summary
2910 | 
2911 | ## [Functions](AshPhoenix.SubdomainPlug.html#functions)
2912 | 
2913 | [live\_tenant(socket, url)](AshPhoenix.SubdomainPlug.html#live_tenant/2)
2914 | 
2915 | # [](AshPhoenix.SubdomainPlug.html#functions)Functions
2916 | 
2917 | [](AshPhoenix.SubdomainPlug.html#live_tenant/2)
2918 | 
2919 | # live\_tenant(socket, url)
2920 | 
2921 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/ash_phoenix/plug/subdomain_plug.ex#L73)
2922 | 
2923 | [Hex Package](https://hex.pm/packages/ash_phoenix/2.1.14) [Hex Preview](https://preview.hex.pm/preview/ash_phoenix/2.1.14) Search HexDocs [Download ePub version](ash_phoenix.epub "ePub version")
2924 | 
2925 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.36.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
2926 | [![ash_phoenix](assets/logo.png)](https://github.com/ash-project/ash_phoenix)
2927 | 
2928 | [ash\_phoenix](https://github.com/ash-project/ash_phoenix)
2929 | 
2930 | v2.1.14
2931 | 
2932 | - Pages
2933 | - Modules
2934 | - Mix Tasks
2935 | 
2936 | <!--THE END-->
2937 | 
2938 | <!--THE END-->
2939 | 
2940 | <!--THE END-->
2941 | 
2942 | Search documentation of ash\_phoenix
2943 | 
2944 | Settings
2945 | 
2946 | # [View Source](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/documentation/dsls/DSL-AshPhoenix.md#L1 "View Source") DSL: AshPhoenix
2947 | 
2948 | An extension to add form builders to the code interface.
2949 | 
2950 | There is currently no DSL for this extension.
2951 | 
2952 | This defines a `form_to_<name>` function for each code interface function. Positional arguments are ignored, given that in forms, all input typically comes from the `params` map.
2953 | 
2954 | The generated function passes all options through to [`AshPhoenix.Form.for_action/3`](AshPhoenix.Form.html#for_action/3)
2955 | 
2956 | Update and destroy actions take the record being updated/destroyed as the first argument.
2957 | 
2958 | For example, given this code interface definition on a domain called `MyApp.Accounts`:
2959 | 
2960 | ```
2961 | resources do
2962 |   resource MyApp.Accounts.User do
2963 |     define :register_with_password, args: [:email, :password]
2964 |     define :update_user, action: :update, args: [:email, :password]
2965 |   end
2966 | end
2967 | ```
2968 | 
2969 | Adding the [`AshPhoenix`](AshPhoenix.html) extension would define `form_to_register_with_password/2`.
2970 | 
2971 | ## [](dsl-ashphoenix.html#usage)Usage
2972 | 
2973 | Without options:
2974 | 
2975 | ```
2976 | MyApp.Accounts.form_to_register_with_password()
2977 | #=> %AshPhoenix.Form{}
2978 | ```
2979 | 
2980 | With options:
2981 | 
2982 | ```
2983 | MyApp.Accounts.form_to_register_with_password(params: %{"email" => "placeholder@email"})
2984 | #=> %AshPhoenix.Form{}
2985 | ```
2986 | 
2987 | With
2988 | 
2989 | ```
2990 | MyApp.Accounts.form_to_update_user(params: %{"email" => "placeholder@email"})
2991 | #=> %AshPhoenix.Form{}
2992 | ```
2993 | 
2994 | [← Previous Page Change Log](changelog.html)
2995 | 
2996 | [Hex Package](https://hex.pm/packages/ash_phoenix/2.1.14) [Hex Preview](https://preview.hex.pm/preview/ash_phoenix/2.1.14) ([current file](https://preview.hex.pm/preview/ash_phoenix/2.1.14/show/documentation/dsls/DSL-AshPhoenix.md)) Search HexDocs [Download ePub version](ash_phoenix.epub "ePub version")
2997 | 
2998 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.36.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
2999 | [![ash_phoenix](assets/logo.png)](https://github.com/ash-project/ash_phoenix)
3000 | 
3001 | [ash\_phoenix](https://github.com/ash-project/ash_phoenix)
3002 | 
3003 | v2.1.14
3004 | 
3005 | - Pages
3006 | - Modules
3007 | - Mix Tasks
3008 | 
3009 | <!--THE END-->
3010 | 
3011 | <!--THE END-->
3012 | 
3013 | <!--THE END-->
3014 | 
3015 | Search documentation of ash\_phoenix
3016 | 
3017 | Settings
3018 | 
3019 | # [View Source](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/documentation/tutorials/getting-started-with-ash-and-phoenix.md#L1 "View Source") Get Started with Ash and Phoenix
3020 | 
3021 | ## [](getting-started-with-ash-and-phoenix.html#goals)Goals
3022 | 
3023 | In this guide we will:
3024 | 
3025 | 1. Create a new Phoenix project
3026 | 2. Setup Ash, AshPhoenix and AshPostgres as dependencies
3027 | 3. Create a basic `Blog.Post` resource
3028 | 4. Create and migrate the database
3029 | 5. Learn how to interact with your resource
3030 | 6. Integrate a minimal Phoenix LiveView with Ash
3031 | 
3032 | ## [](getting-started-with-ash-and-phoenix.html#preparation)Preparation
3033 | 
3034 | - [Install Elixir](https://elixir-lang.org/install.html)
3035 | - [Phoenix - Up and Running Guide](../phoenix/up_and_running.html)
3036 | - [Design Principles](../ash/design-principles.html)
3037 | 
3038 | ## [](getting-started-with-ash-and-phoenix.html#requirements)Requirements
3039 | 
3040 | If you want to follow along yourself, you will need the following things:
3041 | 
3042 | 1. Elixir (1.12 or later) and Erlang (22 or later) installed
3043 | 2. PostgreSQL installed
3044 | 3. A text editor
3045 | 4. A terminal to run the examples
3046 | 
3047 | ## [](getting-started-with-ash-and-phoenix.html#setup)Setup
3048 | 
3049 | ### [](getting-started-with-ash-and-phoenix.html#create-a-new-phoenix-project)Create a New Phoenix Project
3050 | 
3051 | ### [](getting-started-with-ash-and-phoenix.html#install-phoenix)Install Phoenix
3052 | 
3053 | *This section is based on the [Phoenix installation docs](../phoenix/installation.html). For more details go there.*
3054 | 
3055 | First we need to install the Phoenix project generator, then we'll run the generator to create our new project.
3056 | 
3057 | ```
3058 | # install Phoenix project generator
3059 | $ mix archive.install hex phx_new
3060 | 
3061 | # generate Phoenix project
3062 | $ mix igniter.new my_ash_phoenix_app --install ash,ash_phoenix,ash_postgres --with phx.new
3063 | 
3064 | # cd into project
3065 | $ cd my_ash_phoenix_app
3066 | ```
3067 | 
3068 | ### [](getting-started-with-ash-and-phoenix.html#don-t-run-mix-ecto-create)Don't run [`mix ecto.create`](../ecto/3.12.5/Mix.Tasks.Ecto.Create.html)
3069 | 
3070 | Do *not* run [`mix ecto.create`](../ecto/3.12.5/Mix.Tasks.Ecto.Create.html), (as it asks you to) we will do this the Ash way later.
3071 | 
3072 | ### [](getting-started-with-ash-and-phoenix.html#edit-config)Edit Config
3073 | 
3074 | We need to specify the Ash domains that our application uses.
3075 | 
3076 | Add this to your config:
3077 | 
3078 | ```
3079 | # config/config.exs
3080 | 
3081 | import Config
3082 | 
3083 | config :my_ash_phoenix_app,
3084 |   ash_domains: [MyAshPhoenixApp.Blog]
3085 | ```
3086 | 
3087 | ### [](getting-started-with-ash-and-phoenix.html#create-the-domain-and-add-resources)Create the Domain and add Resources
3088 | 
3089 | An Ash domain can be thought of as a [Bounded Context](https://martinfowler.com/bliki/BoundedContext.html) in Domain Driven Design terms and can seen as analogous to a Phoenix context. Put simply, its a way of grouping related resources together. In our case our domain will be called `MyAshPhoenixApp.Blog`.
3090 | 
3091 | An Ash domain points to Ash resources. An Ash domain can point to one or more resources. In our case we will only have a single resource `MyAshPhoenixApp.Blog.Post`. We'll be taking a deeper look into that in the next section.
3092 | 
3093 | For now take a look at the `Blog` domain and the associated resources:
3094 | 
3095 | ```
3096 | # lib/my_ash_phoenix_app/blog/blog.ex
3097 | 
3098 | defmodule MyAshPhoenixApp.Blog do
3099 |   use Ash.Domain
3100 | 
3101 |   resources do
3102 |     resource MyAshPhoenixApp.Blog.Post do
3103 |       # Define an interface for calling resource actions.
3104 |       define :create_post, action: :create
3105 |       define :list_posts, action: :read
3106 |       define :destroy_post, action: :destroy
3107 |       define :get_post, args: [:id], action: :by_id
3108 |     end
3109 |   end
3110 | end
3111 | ```
3112 | 
3113 | ## [](getting-started-with-ash-and-phoenix.html#creating-resources)Creating Resources
3114 | 
3115 | ### [](getting-started-with-ash-and-phoenix.html#creating-the-post-resource)Creating the `Post` Resource
3116 | 
3117 | A resource is a central concept in Ash. In short, a resource is a domain model object in your system. A resource defines the data it holds and defines the actions that can operate on that data.
3118 | 
3119 | When we create `Post` we will place it in `lib/my_ash_phoenix_app/blog/post.ex`. So the structure after making the resource should look like so:
3120 | 
3121 | ```
3122 | lib/
3123 | ├─ my_ash_phoenix_app/
3124 | │  ├─ blog/
3125 | │  │  ├─ blog.ex
3126 | │  │  ├─ post.ex
3127 | ```
3128 | 
3129 | Below is the resource module. Read the comments carefully, every line is explained:
3130 | 
3131 | ```
3132 | # lib/my_ash_phoenix_app/blog/post.ex
3133 | 
3134 | defmodule MyAshPhoenixApp.Blog.Post do
3135 |   # Using Ash.Resource turns this module into an Ash resource.
3136 |   use Ash.Resource,
3137 |     # Tells Ash where the generated code interface belongs
3138 |     domain: MyAshPhoenixApp.Blog,
3139 |     # Tells Ash you want this resource to store its data in Postgres.
3140 |     data_layer: AshPostgres.DataLayer
3141 | 
3142 |   # The Postgres keyword is specific to the AshPostgres module.
3143 |   postgres do
3144 |     # Tells Postgres what to call the table
3145 |     table "posts"
3146 |     # Tells Ash how to interface with the Postgres table
3147 |     repo MyAshPhoenixApp.Repo
3148 |   end
3149 | 
3150 |   actions do
3151 |     # Exposes default built in actions to manage the resource
3152 |     defaults [:read, :destroy]
3153 | 
3154 |     create :create do
3155 |       primary? true
3156 |       # accept title as input
3157 |       accept [:title]
3158 |     end
3159 | 
3160 |     update :update do
3161 |       primary? true
3162 |       # accept content as input
3163 |       accept [:content]
3164 |     end
3165 | 
3166 |     # Defines custom read action which fetches post by id.
3167 |     read :by_id do
3168 |       # This action has one argument :id of type :uuid
3169 |       argument :id, :uuid, allow_nil?: false
3170 |       # Tells us we expect this action to return a single result
3171 |       get? true
3172 |       # Filters the `:id` given in the argument
3173 |       # against the `id` of each element in the resource
3174 |       filter expr(id == ^arg(:id))
3175 |     end
3176 |   end
3177 | 
3178 |   # Attributes are simple pieces of data that exist in your resource
3179 |   attributes do
3180 |     # Add an autogenerated UUID primary key called `:id`.
3181 |     uuid_primary_key :id
3182 |     # Add a string type attribute called `:title`
3183 |     attribute :title, :string do
3184 |       # We don't want the title to ever be `nil`
3185 |       allow_nil? false
3186 |     end
3187 | 
3188 |     # Add a string type attribute called `:content`
3189 |     # If allow_nil? is not specified, then content can be nil
3190 |     attribute :content, :string
3191 |   end
3192 | end
3193 | ```
3194 | 
3195 | ### [](getting-started-with-ash-and-phoenix.html#creating-and-migrating-the-database)Creating and Migrating the Database
3196 | 
3197 | We have specified the resource in Ash. But we have yet to create it in our data layer (in our case Postgres).
3198 | 
3199 | First we need to create our database:
3200 | 
3201 | ```
3202 | $ mix ash.setup
3203 | 
3204 | Running setup for AshPostgres.DataLayer...
3205 | The database for MyAshPhoenixApp.Repo has been created
3206 | 
3207 | 01:23:45.678 [info] Migrations already up
3208 | ```
3209 | 
3210 | Now we need to populate our database. We do this by generating and performing a migration.
3211 | 
3212 | We can use a generator to produce a migration for us. Ash can deduce what needs to go into the migration and do the hard work for us, to do this use the command below:
3213 | 
3214 | ```
3215 | $ mix ash.codegen initial_migration
3216 | 
3217 | # ... don't worry about other files it creates
3218 | 
3219 | Generating Migrations:
3220 | * creating priv/repo/migrations/20230208045101_initial_migration.exs
3221 | ```
3222 | 
3223 | Here is the migration file commented in detail:
3224 | 
3225 | ```
3226 | # priv/repo/migrations/20230208045101_initial_migration.exs
3227 | 
3228 | defmodule MyAshPhoenixApp.Repo.Migrations.InitialMigration do
3229 |   use Ecto.Migration
3230 | 
3231 |   # This function runs when migrating forward
3232 |   def up do
3233 |     # Creates the `:posts` table
3234 |     create table(:posts, primary_key: false) do
3235 |       # Adds primary key attribute `:id` of type `:uuid`
3236 |       # null values are not allowed
3237 |       add :id, :uuid, null: false, default: fragment("gen_random_uuid()"), primary_key: true
3238 | 
3239 |       # Adds attribute `:title` of type `:text`, null values are not allowed
3240 |       add :title, :text, null: false
3241 |       # Adds attribute `:content` of type `:text`, null values are allowed
3242 |       add :content, :text
3243 |     end
3244 |   end
3245 | 
3246 |   # This is the function that runs if you want to rollback the migration.
3247 |   def down do
3248 |     # Deletes the `:posts` table
3249 |     drop table(:posts)
3250 |   end
3251 | end
3252 | ```
3253 | 
3254 | We can run the `up/0` function which will perform the desired operations on the Postgres database. We do this with the migrate command:
3255 | 
3256 | ```
3257 | $ mix ash.migrate
3258 | ```
3259 | 
3260 | > In case you want to drop the database and start over again during development you can use [`mix ash.reset`](../ash/3.4.55/Mix.Tasks.Ash.Reset.html).
3261 | 
3262 | ## [](getting-started-with-ash-and-phoenix.html#interacting-with-your-resources)Interacting with your Resources
3263 | 
3264 | **All interaction with your resource attributes always occur through an action**. In our resource we are using the default actions for `:create, :read, :update, :destroy` along with a custom action `:by_id`.
3265 | 
3266 | `:create` and `:update` and `:destroy` actions require a changeset. Ash changesets are conceptually similar to [Ecto changesets](https://hexdocs.pm/ecto/Ecto.Changeset.html). They're data structures which represent an intended change to an Ash resource and provide validation.
3267 | 
3268 | The `:read` action takes a query instead of a changeset.
3269 | 
3270 | Below is the most verbose way of calling your resource. All other ways of interaction are some kind of shorthand of these. This means at some point a changeset is being created and passed to the domain, even if it's encapsulated within another function.
3271 | 
3272 | ```
3273 | # create post
3274 | new_post =
3275 |   MyAshPhoenixApp.Blog.Post
3276 |   |> Ash.Changeset.for_create(:create, %{title: "hello world"})
3277 |   |> Ash.create!()
3278 | 
3279 | # read all posts
3280 | MyAshPhoenixApp.Blog.Post
3281 | |> Ash.Query.for_read(:read)
3282 | |> Ash.read!()
3283 | 
3284 | # get single post by id
3285 | MyAshPhoenixApp.Blog.Post
3286 | |> Ash.Query.for_read(:by_id, %{id: new_post.id})
3287 | |> Ash.read_one!()
3288 | 
3289 | # update post
3290 | updated_post =
3291 |   new_post
3292 |   |> Ash.Changeset.for_update(:update, %{content: "hello to you too!"})
3293 |   |> Ash.update!()
3294 | 
3295 | # delete post
3296 | new_post
3297 | |> Ash.Changeset.for_destroy(:destroy)
3298 | |> Ash.destroy!()
3299 | ```
3300 | 
3301 | As stated above, this is verbose so Ash has a built in shortcut - The `code_interface`. You may notice this has already been done in your `Post` resource inside of the domain module.
3302 | 
3303 | ### [](getting-started-with-ash-and-phoenix.html#you-can-call-code-interfaces-whatever-you-like)you can call code interfaces whatever you like
3304 | 
3305 | The function name doesn't have to match the action name in any way. You could also write:
3306 | 
3307 | ```
3308 | define :make_post, action: :create
3309 | ```
3310 | 
3311 | That's perfectly valid and could be called via `Blog.make_post/2`.
3312 | 
3313 | Now we can call our resource like so:
3314 | 
3315 | ```
3316 | # create post
3317 | new_post = MyAshPhoenixApp.Blog.create_post!(%{title: "hello world"})
3318 | 
3319 | # read post
3320 | MyAshPhoenixApp.Blog.list_posts!()
3321 | 
3322 | # get post by id
3323 | MyAshPhoenixApp.Blog.get_post!(new_post.id)
3324 | 
3325 | # update post
3326 | updated_post = MyAshPhoenixApp.Blog.update_post!(new_post, %{content: "hello to you too!"})
3327 | 
3328 | # delete post
3329 | MyAshPhoenixApp.Blog.destroy_post!(updated_post)
3330 | ```
3331 | 
3332 | Now isn't that more convenient?
3333 | 
3334 | ### [](getting-started-with-ash-and-phoenix.html#raising-and-non-raising-functions)raising and non-raising functions
3335 | 
3336 | All functions that interact with an Ash resource have a raising and non-raising version. For example there are two create functions `create/2` and `create!/2`. `create/2` returns `{:ok, resource}` or `{:error, reason}`. `create!/2` will return just the record on success and will raise an error on failure.
3337 | 
3338 | ## [](getting-started-with-ash-and-phoenix.html#connecting-your-resource-to-a-phoenix-liveview)Connecting your Resource to a Phoenix LiveView
3339 | 
3340 | Now we know how to interact with our resource, we can generate a liveview for it! let's run [`mix ash_phoenix.gen.live`](Mix.Tasks.AshPhoenix.Gen.Live.html) to generate a liveview! Run the following command, declining to name your actor, accepting any other default values, and following the instructions listed at the end for adding the liveview to your router.
3341 | 
3342 | ```
3343 | mix ash_phoenix.gen.live --domain MyAshPhoenixApp.Blog --resource MyAshPhoenixApp.Blog.Post --resourceplural posts
3344 | ```
3345 | 
3346 | Now, start the web server by running [`mix phx.server`](../phoenix/1.7.18/Mix.Tasks.Phx.Server.html). Then, visit the posts route that you added in your browser to see what we have just created.
3347 | 
3348 | You can see how using functions created by our `code_interface` makes it easy to integrate Ash with Phoenix.
3349 | 
3350 | You may also notice this is the first time we've used the AshPhoenix library. The AshPhoenix library contains utilities to help Ash integrate with Phoenix and LiveView Seamlessly. One of these utilities is [`AshPhoenix.Form`](AshPhoenix.Form.html) which can automatically produce changesets to be used in the forms.
3351 | 
3352 | That's it for this guide. We've gone from 0 to a fully working Phoenix App using Ash. To get a closer look, see the accompanying repo [here](https://github.com/team-alembic/my_ash_phoenix_project).
3353 | 
3354 | ## [](getting-started-with-ash-and-phoenix.html#where-to-next)Where to Next?
3355 | 
3356 | We are really just scratching the surface of what can be done in Ash. Look below for what to look at next.
3357 | 
3358 | ### [](getting-started-with-ash-and-phoenix.html#continue-learning)Continue Learning
3359 | 
3360 | There's a few places you can go to learn more about how to use ash:
3361 | 
3362 | - Read more about how to query the data in your resources - [`Ash.Query`](../ash/3.4.55/Ash.Query.html)
3363 | - [Dig deeper into actions.](../ash/actions.html)
3364 | - [Study resource relationship management](https://hexdocs.pm/ash/managing-relationships.html)
3365 | 
3366 | ### [](getting-started-with-ash-and-phoenix.html#ash-authentication-ash-authentication-phoenix)Ash Authentication &amp; Ash Authentication Phoenix
3367 | 
3368 | See the power Ash can bring to your web app or API. [Get authentication working in minutes](../ash_authentication_phoenix/get-started.html).
3369 | 
3370 | ### [](getting-started-with-ash-and-phoenix.html#add-an-api-or-two)Add an API (or two)
3371 | 
3372 | Check out the [AshJsonApi](../ash_json_api/getting-started-with-ash-json-api.html) and [AshGraphql](../ash_graphql/getting-started-with-graphql.html) extensions to effortlessly build APIs around your resources.
3373 | 
3374 | [← Previous Page Home](readme.html)
3375 | 
3376 | [Next Page → Union Forms](union-forms.html)
3377 | 
3378 | [Hex Package](https://hex.pm/packages/ash_phoenix/2.1.14) [Hex Preview](https://preview.hex.pm/preview/ash_phoenix/2.1.14) ([current file](https://preview.hex.pm/preview/ash_phoenix/2.1.14/show/documentation/tutorials/getting-started-with-ash-and-phoenix.md)) Search HexDocs [Download ePub version](ash_phoenix.epub "ePub version")
3379 | 
3380 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.36.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
3381 | [![ash_phoenix](assets/logo.png)](https://github.com/ash-project/ash_phoenix)
3382 | 
3383 | [ash\_phoenix](https://github.com/ash-project/ash_phoenix)
3384 | 
3385 | v2.1.14
3386 | 
3387 | - Pages
3388 | - Modules
3389 | - Mix Tasks
3390 | 
3391 | <!--THE END-->
3392 | 
3393 | <!--THE END-->
3394 | 
3395 | <!--THE END-->
3396 | 
3397 | Search documentation of ash\_phoenix
3398 | 
3399 | Settings
3400 | 
3401 | # [View Source](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/mix/tasks/ash_phoenix.gen.html.ex#L1 "View Source") mix ash\_phoenix.gen.html (ash\_phoenix v2.1.14)
3402 | 
3403 | This task renders .ex and .heex templates and copies them to specified directories.
3404 | 
3405 | ## [](Mix.Tasks.AshPhoenix.Gen.Html.html#module-positional-arguments)Positional Arguments
3406 | 
3407 | - `domain` - The domain (e.g. "Shop").
3408 | - `resource` - The resource (e.g. "Product").
3409 | 
3410 | ## [](Mix.Tasks.AshPhoenix.Gen.Html.html#module-options)Options
3411 | 
3412 | - `--resource-plural` - The plural resource name (e.g. "products")
3413 | 
3414 | mix ash\_phoenix.gen.html MyApp.Shop MyApp.Shop.Product --plural-name products
3415 | 
3416 | # [](Mix.Tasks.AshPhoenix.Gen.Html.html#summary)Summary
3417 | 
3418 | ## [Functions](Mix.Tasks.AshPhoenix.Gen.Html.html#functions)
3419 | 
3420 | [run(args)](Mix.Tasks.AshPhoenix.Gen.Html.html#run/1)
3421 | 
3422 | Callback implementation for [`Mix.Task.run/1`](../mix/Mix.Task.html#c:run/1).
3423 | 
3424 | # [](Mix.Tasks.AshPhoenix.Gen.Html.html#functions)Functions
3425 | 
3426 | [](Mix.Tasks.AshPhoenix.Gen.Html.html#run/1)
3427 | 
3428 | # run(args)
3429 | 
3430 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/mix/tasks/ash_phoenix.gen.html.ex#L14)
3431 | 
3432 | Callback implementation for [`Mix.Task.run/1`](../mix/Mix.Task.html#c:run/1).
3433 | 
3434 | [Hex Package](https://hex.pm/packages/ash_phoenix/2.1.14) [Hex Preview](https://preview.hex.pm/preview/ash_phoenix/2.1.14) Search HexDocs [Download ePub version](ash_phoenix.epub "ePub version")
3435 | 
3436 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.36.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
3437 | [![ash_phoenix](assets/logo.png)](https://github.com/ash-project/ash_phoenix)
3438 | 
3439 | [ash\_phoenix](https://github.com/ash-project/ash_phoenix)
3440 | 
3441 | v2.1.14
3442 | 
3443 | - Pages
3444 | - Modules
3445 | - Mix Tasks
3446 | 
3447 | <!--THE END-->
3448 | 
3449 | <!--THE END-->
3450 | 
3451 | <!--THE END-->
3452 | 
3453 | Search documentation of ash\_phoenix
3454 | 
3455 | Settings
3456 | 
3457 | # [View Source](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/mix/tasks/ash_phoenix.gen.live.ex#L2 "View Source") mix ash\_phoenix.gen.live (ash\_phoenix v2.1.14)
3458 | 
3459 | Generates liveviews for a given domain and resource.
3460 | 
3461 | The domain and resource must already exist, this task does not define them.
3462 | 
3463 | ## [](Mix.Tasks.AshPhoenix.Gen.Live.html#module-example)Example
3464 | 
3465 | ```
3466 | mix ash_phoenix.gen.live --domain ExistingDomainName --resource ExistingResourceName --resourceplural ExistingResourceNames
3467 | ```
3468 | 
3469 | ## [](Mix.Tasks.AshPhoenix.Gen.Live.html#module-options)Options
3470 | 
3471 | - `--domain` - Existing domain
3472 | - `--resource` - Existing resource
3473 | - `--resourceplural` - Plural resource name
3474 | 
3475 | # [](Mix.Tasks.AshPhoenix.Gen.Live.html#summary)Summary
3476 | 
3477 | ## [Functions](Mix.Tasks.AshPhoenix.Gen.Live.html#functions)
3478 | 
3479 | [igniter(igniter, argv)](Mix.Tasks.AshPhoenix.Gen.Live.html#igniter/2)
3480 | 
3481 | Callback implementation for [`Igniter.Mix.Task.igniter/2`](../igniter/0.5.9/Igniter.Mix.Task.html#c:igniter/2).
3482 | 
3483 | # [](Mix.Tasks.AshPhoenix.Gen.Live.html#functions)Functions
3484 | 
3485 | [](Mix.Tasks.AshPhoenix.Gen.Live.html#igniter/2)
3486 | 
3487 | # igniter(igniter, argv)
3488 | 
3489 | [](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/lib/mix/tasks/ash_phoenix.gen.live.ex#L43)
3490 | 
3491 | Callback implementation for [`Igniter.Mix.Task.igniter/2`](../igniter/0.5.9/Igniter.Mix.Task.html#c:igniter/2).
3492 | 
3493 | [Hex Package](https://hex.pm/packages/ash_phoenix/2.1.14) [Hex Preview](https://preview.hex.pm/preview/ash_phoenix/2.1.14) Search HexDocs [Download ePub version](ash_phoenix.epub "ePub version")
3494 | 
3495 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.36.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
3496 | [![ash_phoenix](assets/logo.png)](https://github.com/ash-project/ash_phoenix)
3497 | 
3498 | [ash\_phoenix](https://github.com/ash-project/ash_phoenix)
3499 | 
3500 | v2.1.14
3501 | 
3502 | - Pages
3503 | - Modules
3504 | - Mix Tasks
3505 | 
3506 | <!--THE END-->
3507 | 
3508 | <!--THE END-->
3509 | 
3510 | <!--THE END-->
3511 | 
3512 | Search documentation of ash\_phoenix
3513 | 
3514 | Settings
3515 | 
3516 | # [View Source](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/documentation/topics/nested-forms.md#L1 "View Source") Nested Forms
3517 | 
3518 | Make sure you're familiar with the basics of [`AshPhoenix.Form`](AshPhoenix.Form.html) before reading this guide.
3519 | 
3520 | When we talk about "nested" or "related" forms, we mean sets of form inputs that are for resource actions for related or embedded resources.
3521 | 
3522 | For example, you might have a form for creating a "business" that can also include multiple "locations". In some cases, you may have buttons to add or remove from a list of nested forms, you may be able to drag and drop to reorder forms, etc. In other cases, the form may just be for one related thing, think a form for updating a "user" that also contains inputs for its associated "profile".
3523 | 
3524 | ## [](nested-forms.html#defining-the-structure)Defining the structure
3525 | 
3526 | ### [](nested-forms.html#inferring-from-the-action)Inferring from the action
3527 | 
3528 | [`AshPhoenix.Form`](AshPhoenix.Form.html) automatically infers what "nested forms" are available, based on introspecting actions which use `change manage_relationship`. For example, in the following action:
3529 | 
3530 | ```
3531 | # on a `MyApp.Operations.Business` resource
3532 | create :create do
3533 |   accept [:name]
3534 | 
3535 |   argument :locations, {:array, :map}
3536 | 
3537 |   change manage_relationship(:locations, type: :create)
3538 | end
3539 | ```
3540 | 
3541 | With this action, you could submit an input like so:
3542 | 
3543 | ```
3544 | %{name: "Wally World", locations: [%{name: "HQ", address: "1 hq street"}]}
3545 | ```
3546 | 
3547 | [`AshPhoenix.Form`](AshPhoenix.Form.html) will look at the action, allowing you to use [`Phoenix`](../phoenix/1.7.18/Phoenix.html)'s `<.inputs_for` component for `locations`. Here is what it might look like in practice:
3548 | 
3549 | ```
3550 | <.simple_form for={@form} phx-change="validate" phx-submit="submit">
3551 |   <.input field={@form[:email]} />
3552 | 
3553 |   <.inputs_for :let={location} field={@form[:locations]}>
3554 |     <.input field={location[:name]} />
3555 |   </.inputs_for>
3556 | </.form>
3557 | ```
3558 | 
3559 | To turn this automatic behavior off, you can specify `forms: [auto?: false]` when creating the form.
3560 | 
3561 | ### [](nested-forms.html#manually-defining-nested-forms)Manually defining nested forms
3562 | 
3563 | You can manually specify nested form configurations using the `forms` option.
3564 | 
3565 | For example:
3566 | 
3567 | ```
3568 | AshPhoenix.Form.for_create(
3569 |   MyApp.Operations.Business, 
3570 |   :create, 
3571 |   forms: [
3572 |     locations: [
3573 |       type: :list,
3574 |       resource: MyApp.Operations.Location,
3575 |       create_action: :create
3576 |     ]
3577 |   ]
3578 | )
3579 | ```
3580 | 
3581 | You should prefer to use the automatic form definition wherever possible, but this exists as an escape hatch to customize configuration.
3582 | 
3583 | ## [](nested-forms.html#updating-existing-data)Updating existing data
3584 | 
3585 | You should be sure to load any relationships that are necessary for your `manage_relationship`s when you want to update the nested items. For example, if the form above was for an update action, you may want to allow updating the existing locations all in a single form. [`AshPhoenix.Form`](AshPhoenix.Form.html) will show a form for each existing location, but only if the locations are loaded on the business already. For example:
3586 | 
3587 | ```
3588 | business = Ash.load!(business, :locations)
3589 | 
3590 | form = AshPhoenix.Form.for_update(business, :update)
3591 | ```
3592 | 
3593 | ### [](nested-forms.html#not-using-tailwind)Not using tailwind?
3594 | 
3595 | If you're not using tailwind, you'll need to replace `class="hidden"` in the examples below with something else. In standard HTML, you'd do `<input .... hidden />`. As long as the checkbox is hidden, you're good!
3596 | 
3597 | ## [](nested-forms.html#adding-nested-forms)Adding nested forms
3598 | 
3599 | There are two ways to add nested forms.
3600 | 
3601 | ### [](nested-forms.html#the-_add_-checkbox)The `_add_*` checkbox
3602 | 
3603 | ```
3604 | <.simple_form for={@form} phx-change="validate" phx-submit="submit">
3605 |   <.input field={@form[:email]} />
3606 | 
3607 |   <.inputs_for :let={location} field={@form[:locations]}>
3608 |     <.input field={location[:name]} />
3609 |   </.inputs_for>
3610 | 
3611 |   <label>
3612 |     <input
3613 |       type="checkbox"
3614 |       name={"#{@form.name}[_add_locations]"}
3615 |       value="end"
3616 |       class="hidden"
3617 |     />
3618 |     <.icon name="hero-plus" />
3619 |   </label>
3620 | </.form>
3621 | ```
3622 | 
3623 | This checkbox, when checked, will add a parameter like `form[_add_locations]=end`. When [`AshPhoenix.Form`](AshPhoenix.Form.html) is handling nested forms, it will see that and append an empty form at the end. Valid values are `"start"`, `"end"` and an index, i.e `"3"`, in which case the new form will be inserted at that index.
3624 | 
3625 | ### [](nested-forms.html#but-the-checkbox-is-hidden-what-gives)But the checkbox is hidden, what gives?
3626 | 
3627 | If you're anything like me, the label + checkbox combo above may confuse you at first sight. When you have a checkbox inside of a label, clicking on the label [counts as clicking the checkbox itself](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox#providing_a_bigger_hit_area_for_your_checkboxes)!
3628 | 
3629 | ### [](nested-forms.html#ashphoenix-form-add_form)`AshPhoenix.Form.add_form`
3630 | 
3631 | In some cases, you may want to add a form either in a way that can't be triggered by a checkbox or that requires some additional data (like non-empty starting params). In those cases, you can use a button and a `handle_event` For example:
3632 | 
3633 | ```
3634 | <.simple_form for={@form} phx-change="validate" phx-submit="submit">
3635 |   <.input field={@form[:email]} />
3636 | 
3637 |   <.inputs_for :let={location} field={@form[:locations]}>
3638 |     <.input field={location[:name]} />
3639 |   </.inputs_for>
3640 | 
3641 |   <.button type="button" phx-click="add-form" phx-value-path={@form.name <> "[locations]"}>
3642 |     <.icon name="hero-plus" />
3643 |   </.button>
3644 | </.form>
3645 | ```
3646 | 
3647 | ### [](nested-forms.html#whats-with-form-name-locations)whats with `@form.name <> "[locations]"`
3648 | 
3649 | By always using a path "relative" to the root form, we can handle cases where we are adding a form to a multiply-nested form. So the path could be somethign like locations\[0]\[addresses]\[1]. The event handler has to know exactly where we are adding a form. In the example above, we *could* just say `add_form(form, :locations)`. It would be simpler, but we want to highlight how to work with potentially deeply nested data.
3650 | 
3651 | ```
3652 | def handle_event("add-form", %{"path" => path}, socket) do
3653 |   form = AshPhoenix.Form.add_form(socket.assigns.form, path, params: %{
3654 |     address: "Put your address here!"
3655 |   })
3656 | 
3657 |   {:noreply, assign(socket, :form, form)}
3658 | end
3659 | ```
3660 | 
3661 | ## [](nested-forms.html#removing-nested-forms)Removing nested forms
3662 | 
3663 | Just like adding nested forms, there are two ways to *remove* nested forms.
3664 | 
3665 | ### [](nested-forms.html#using-the-_drop_-checkbox)Using the `_drop_*` checkbox
3666 | 
3667 | The `_drop_*` checkbox uses checkboxes which add form indices to a list that should be *removed* from the list. For example, given the following:
3668 | 
3669 | ```
3670 | <.simple_form for={@form} phx-change="validate" phx-submit="submit">
3671 |   <.input field={@form[:email]} />
3672 | 
3673 |   <.inputs_for :let={location} field={@form[:locations]}>
3674 |     <.input field={location[:name]} />
3675 | 
3676 |     <label>
3677 |       <input
3678 |         type="checkbox"
3679 |         name={"#{@form.name}[_drop_locations][]"}
3680 |         value={location_form.index}
3681 |         class="hidden"
3682 |       />
3683 | 
3684 |       <.icon name="hero-x-mark" />
3685 |     </label>
3686 |   </.inputs_for>
3687 | </.form>
3688 | ```
3689 | 
3690 | When the checkbox is checked, the server sees:
3691 | 
3692 | ```
3693 | %{"form" => %{"_drop_locations" => ["0"]}}
3694 | ```
3695 | 
3696 | We use this information to automatically remove the item at that index on validate.
3697 | 
3698 | ### [](nested-forms.html#using-ashphoenix-form-remove_form)Using `AshPhoenix.Form.remove_form`
3699 | 
3700 | Just like adding forms, there is a manual way to remove forms. In this case we pass the full path to the form being removed.
3701 | 
3702 | ```
3703 | <.simple_form for={@form} phx-change="validate" phx-submit="submit">
3704 |   <.input field={@form[:email]} />
3705 | 
3706 |   <.inputs_for :let={location} field={@form[:locations]}>
3707 |     <.input field={location[:name]} />
3708 | 
3709 |     <.button type="button" phx-click="remove-form" phx-value-path={location.name}>
3710 |       <.icon name="hero-x-mark" />
3711 |     </.button>
3712 |   </.inputs_for>
3713 | </.form>
3714 | ```
3715 | 
3716 | ```
3717 | def handle_event("remove-form", %{"path" => path}, socket) do
3718 |   form = AshPhoenix.Form.remove_form(socket.assigns.form, path)
3719 | 
3720 |   {:noreply, assign(socket, :form, form)}
3721 | end
3722 | ```
3723 | 
3724 | ## [](nested-forms.html#sorting-nested-forms)Sorting nested forms
3725 | 
3726 | Just like adding and removing forms, there are two ways to *sort* nested forms too!
3727 | 
3728 | ### [](nested-forms.html#using-_sort_-checkboxes)Using `_sort_*` checkboxes
3729 | 
3730 | This method is useful when combined with something like [`sortable.js`](https://sortablejs.github.io/Sortable/) to allow for dragging and dropping on the front end.
3731 | 
3732 | ### [](nested-forms.html#the-order_is_key-option)the `order_is_key` option
3733 | 
3734 | If you are working with a sorted relationship, you will likely want to couple it with the `order_is_key` option of `managed_relationships`. This writes the order of items in the list of inputs into each input, as if it was provided as an input
3735 | 
3736 | `change manage_relationship(:locations, type: :direct_control, order_is_key: :position)` In the above example, if you provided a list of inputs like `[%{address: "foo"}, %{address: "bar"}]`, it would first be converted into `[%{address: "foo, order: 0}, %{address: "bar", order: 1}]` before being processed.
3737 | 
3738 | Lets say you had the following `Sortable` hook in your `app.js`
3739 | 
3740 | ```
3741 | import Sortable from "sortablejs"
3742 | 
3743 | export const Sortable = {
3744 |   mounted() {
3745 |     new Sortable(this.el, {
3746 |       animation: 150,
3747 |       draggable: '[data-sortable="true"]',
3748 |       ghostClass: "bg-yellow-100",
3749 |       dragClass: "shadow-2xl",
3750 |       onEnd: (evt) => {
3751 |         this.el.closest("form").querySelector("input").dispatchEvent(new Event("input", {bubbles: true}))
3752 |       }
3753 |     })
3754 |   }
3755 | }
3756 | ...
3757 | 
3758 | let Hooks = {}
3759 | 
3760 | Hooks.Sortable = Sortable
3761 | ```
3762 | 
3763 | You could use the `_sort_*` checkbox in each nested form like so:
3764 | 
3765 | ```
3766 | <.simple_form for={@form} phx-change="validate" phx-submit="submit">
3767 |   <.input field={@form[:email]} />
3768 | 
3769 |   <div id="location-list" phx-hook="Sortable">
3770 |     <.inputs_for :let={location} field={@form[:locations]}>
3771 |       <div data-sortable="true">
3772 |         <input
3773 |           type="hidden"
3774 |           name={"#{@form.name}[_sort_locations][]"}
3775 |           value={location_form.index}
3776 |         />
3777 | 
3778 |         <.input field={location[:name]} />
3779 |       </div>
3780 |     </.inputs_for>
3781 | </.form>
3782 | ```
3783 | 
3784 | In this case you'd drag the entire div. `sortable.js` supports all kinds of useful features, like drag handles. See [their docs](https://sortablejs.github.io/Sortable/) for more.
3785 | 
3786 | Now, lets say you were to drag the second form above the first form, the server would see the params as:
3787 | 
3788 | ```
3789 | %{"form" => %{"_sort_locations" => ["1", "0"]}}
3790 | ```
3791 | 
3792 | [`AshPhoenix.Form`](AshPhoenix.Form.html) would then sort the nested forms accordingly.
3793 | 
3794 | ### [](nested-forms.html#using-ashphoenix-form-sort_forms-3)Using [`AshPhoenix.Form.sort_forms/3`](AshPhoenix.Form.html#sort_forms/3)
3795 | 
3796 | The manual way is using [`AshPhoenix.Form.sort_forms/3`](AshPhoenix.Form.html#sort_forms/3). This can be used to move a specific element up or down, or to sort all forms. `sortable.js` can be used in such a way that it provides the full sorting back to your server.
3797 | 
3798 | #### Providing a full sort order
3799 | 
3800 | This could be used to send a `handle_event` that gives you a list of indices in a new order. An example of that setup can be seen [here](https://fullstackphoenix.com/tutorials/sortable-js-phoenix-liveview). Keep in mind that you'll want to adjust the method to extract a field from each element of the current index, using something like `data-current-index={location_form.index}` to store the index.
3801 | 
3802 | `indices` might look something like this: `["0", "1", "3", "2"]`
3803 | 
3804 | ```
3805 | def handle_event("update-sorting", %{"path" => path, "indices" => indices}, socket) do
3806 |   form = AshPhoenix.Form.sort_forms(socket, path, indices)
3807 |   {:noreply, assign(socket, form: form)}
3808 | end
3809 | ```
3810 | 
3811 | #### Moving a specific form up
3812 | 
3813 | If you wanted up/down buttons, you could use event handlers like the following.
3814 | 
3815 | ```
3816 | def handle_event("move-up", %{"path" => form_to_move}, socket) do
3817 |   # decrement typically means "move up" visually
3818 |   # because forms are rendered down the page ascending
3819 |   form = AshPhoenix.Form.sort_forms(socket, form_to_move, :decrement)
3820 |   {:noreply, assign(socket, form: form)}
3821 | end
3822 | 
3823 | def handle_event("move-down", %{"path" => form_to_move}, socket) do
3824 |   # increment typically means "move down" visually
3825 |   # because forms are rendered down the page ascending
3826 |   form = AshPhoenix.Form.sort_forms(socket, form_to_move, :increment)
3827 |   {:noreply, assign(socket, form: form)}
3828 | end
3829 | ```
3830 | 
3831 | ## [](nested-forms.html#putting-it-all-together)Putting it all together
3832 | 
3833 | Lets look at what it looks like with all of the checkbox-based features in one:
3834 | 
3835 | ```
3836 | defmodule MyApp.MyForm do
3837 |   use MyAppWeb, :live_view
3838 | 
3839 |   def render(assigns) do
3840 |     ~H"""
3841 |     <.simple_form for={@form} phx-change="validate" phx-submit="submit">
3842 |       <.input field={@form[:email]} />
3843 | 
3844 |       <!-- Use sortable.js to allow sorting nested input -->
3845 |       <div id="location-list" phx-hook="Sortable">
3846 |         <.inputs_for :let={location} field={@form[:locations]}>
3847 |           <!-- inputs each nested location -->
3848 |           <div data-sortable="true">
3849 |             <!-- AshPhoenix.Form automatically applies this sort -->
3850 |             <input
3851 |               type="hidden"
3852 |               name={"#{@form.name}[_sort_locations][]"}
3853 |               value={location_form.index}
3854 |             />
3855 | 
3856 |             <.input field={location[:name]} />
3857 | 
3858 |             <!-- AshPhoenix.Form automatically removes items when checked -->
3859 |             <label>
3860 |               <input
3861 |                 type="checkbox"
3862 |                 name={"#{@form.name}[_drop_locations][]"}
3863 |                 value={location_form.index}
3864 |                 class="hidden"
3865 |               />
3866 | 
3867 |               <.icon name="hero-x-mark" />
3868 |             </label>
3869 |           </div>
3870 |         </.inputs_for>
3871 | 
3872 |         <!-- AshPhoenix.Form automatically appends a new item when checked -->
3873 |         <label>
3874 |           <input
3875 |             type="checkbox"
3876 |             name={"#{@form.name}[_add_locations]"}
3877 |             value="end"
3878 |             class="hidden"
3879 |           />
3880 |           <.icon name="hero-plus" />
3881 |         </label>
3882 |       </div>
3883 |     </.form>
3884 |     """
3885 |   end
3886 | 
3887 |   def mount(_params, _session, socket) do
3888 |     {:ok, assign(socket, form: MyApp.Operations.form_to_create_business())}
3889 |   end
3890 | 
3891 |   def handle_event(socket, "validate", %{"form" => params}) do
3892 |     {:noreply, assign(socket, :form, AshPhoenix.Form.validate(socket.assigns.form, params))}
3893 |   end
3894 | 
3895 |   def handle_event(socket, "submit", %{"form" => params}) do
3896 |     case AshPhoenix.Form.submit(socket.assigns.form, params: params) do
3897 |       {:ok, business} ->
3898 |         socket =
3899 |           socket
3900 |           |> put_flash(:success, "Business created successfully")
3901 |           |> push_navigate(to: ~p"/businesses/#{business.id}")
3902 | 
3903 |         {:noreply, socket}
3904 | 
3905 |       {:error, form} ->
3906 |         {:noreply, assign(socket, :form, form)}
3907 |     end
3908 |   end
3909 | end
3910 | ```
3911 | 
3912 | [← Previous Page Union Forms](union-forms.html)
3913 | 
3914 | [Next Page → Change Log](changelog.html)
3915 | 
3916 | [Hex Package](https://hex.pm/packages/ash_phoenix/2.1.14) [Hex Preview](https://preview.hex.pm/preview/ash_phoenix/2.1.14) ([current file](https://preview.hex.pm/preview/ash_phoenix/2.1.14/show/documentation/topics/nested-forms.md)) Search HexDocs [Download ePub version](ash_phoenix.epub "ePub version")
3917 | 
3918 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.36.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
3919 | [![ash_phoenix](assets/logo.png)](https://github.com/ash-project/ash_phoenix)
3920 | 
3921 | [ash\_phoenix](https://github.com/ash-project/ash_phoenix)
3922 | 
3923 | v2.1.14
3924 | 
3925 | - Pages
3926 | - Modules
3927 | - Mix Tasks
3928 | 
3929 | <!--THE END-->
3930 | 
3931 | <!--THE END-->
3932 | 
3933 | <!--THE END-->
3934 | 
3935 | Search documentation of ash\_phoenix
3936 | 
3937 | Settings
3938 | 
3939 | # [View Source](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/README.md#L1 "View Source") Home
3940 | 
3941 | ![Logo](https://github.com/ash-project/ash/blob/main/logos/cropped-for-header-black-text.png?raw=true#gh-light-mode-only) ![Logo](https://github.com/ash-project/ash/blob/main/logos/cropped-for-header-white-text.png?raw=true#gh-dark-mode-only)
3942 | 
3943 | ![Elixir CI](https://github.com/ash-project/ash_phoenix/workflows/CI/badge.svg) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Hex version badge](https://img.shields.io/hexpm/v/ash_phoenix.svg)](https://hex.pm/packages/ash_phoenix) [![Hexdocs badge](https://img.shields.io/badge/docs-hexdocs-purple)](../ash_phoenix.html)
3944 | 
3945 | # AshPhoenix
3946 | 
3947 | Welcome! This is the package for integrating [Phoenix Framework](https://www.phoenixframework.org) and [Ash Framework](../ash.html). It provides tools for integrating with Phoenix forms ([`AshPhoenix.Form`](AshPhoenix.Form.html)), Phoenix LiveViews ([`AshPhoenix.LiveView`](AshPhoenix.LiveView.html)) and more.
3948 | 
3949 | ## [](readme.html#installation)Installation
3950 | 
3951 | Add `ash_phoenix` to your list of dependencies in `mix.exs`:
3952 | 
3953 | ```
3954 | {:ash_phoenix, "~> 2.1.14"}
3955 | ```
3956 | 
3957 | ## [](readme.html#whats-in-the-box)Whats in the box?
3958 | 
3959 | - [`AshPhoenix.Form`](AshPhoenix.Form.html) - A form data structure for using resource actions with phoenix forms
3960 | - [`AshPhoenix.Form.Auto`](AshPhoenix.Form.Auto.html) - Tools to automatically determine nested form structures based on calls to `manage_relationship` for an action.
3961 | - [`AshPhoenix.FilterForm`](AshPhoenix.FilterForm.html) - A form data structure for building filter statements
3962 | - [`AshPhoenix.LiveView`](AshPhoenix.LiveView.html) - Helpers for querying data and integrating changes
3963 | - [`AshPhoenix.SubdomainPlug`](AshPhoenix.SubdomainPlug.html) - A plug to determine a tenant using subdomains for multitenancy
3964 | - [`AshPhoenix.FormData.Error`](AshPhoenix.FormData.Error.html) - A protocol to allow errors to be rendered in forms
3965 | - [`Phoenix.HTML.Safe`](../phoenix_html/4.2.0/Phoenix.HTML.Safe.html) implementations for [`Ash.CiString`](../ash/3.4.55/Ash.CiString.html), [`Ash.NotLoaded`](../ash/3.4.55/Ash.NotLoaded.html) and [`Decimal`](../decimal/2.3.0/Decimal.html)
3966 | - [`mix ash_phoenix.gen.live`](Mix.Tasks.AshPhoenix.Gen.Live.html) for generating liveview modules
3967 | - [`mix ash_phoenix.gen.html`](Mix.Tasks.AshPhoenix.Gen.Html.html) for generating controllers and views
3968 | 
3969 | ## [](readme.html#tutorials)Tutorials
3970 | 
3971 | - [Getting Started with Ash and Phoenix](getting-started-with-ash-and-phoenix.html)
3972 | 
3973 | ## [](readme.html#topics)Topics
3974 | 
3975 | - [Union Forms](union-forms.html)
3976 | - [Nested Forms](nested-forms.html)
3977 | 
3978 | [← Previous Page API Reference](api-reference.html)
3979 | 
3980 | [Next Page → Get Started with Ash and Phoenix](getting-started-with-ash-and-phoenix.html)
3981 | 
3982 | [Hex Package](https://hex.pm/packages/ash_phoenix/2.1.14) [Hex Preview](https://preview.hex.pm/preview/ash_phoenix/2.1.14) ([current file](https://preview.hex.pm/preview/ash_phoenix/2.1.14/show/README.md)) Search HexDocs [Download ePub version](ash_phoenix.epub "ePub version")
3983 | 
3984 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.36.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
3985 | [![ash_phoenix](assets/logo.png)](https://github.com/ash-project/ash_phoenix)
3986 | 
3987 | [ash\_phoenix](https://github.com/ash-project/ash_phoenix)
3988 | 
3989 | v2.1.14
3990 | 
3991 | - Pages
3992 | - Modules
3993 | - Mix Tasks
3994 | 
3995 | <!--THE END-->
3996 | 
3997 | <!--THE END-->
3998 | 
3999 | <!--THE END-->
4000 | 
4001 | Search documentation of ash\_phoenix
4002 | 
4003 | Settings
4004 | 
4005 | # [View Source](https://github.com/ash-project/ash_phoenix/blob/v2.1.14/documentation/topics/union-forms.md#L1 "View Source") Union Forms
4006 | 
4007 | When building a form for a union, you use `inputs_for` as normal, but a few things are done for you under the hood.
4008 | 
4009 | Lets take this example union:
4010 | 
4011 | ```
4012 | defmodule NormalContent do
4013 |   use Ash.Resource, data_layer: :embedded
4014 | 
4015 |   attributes do
4016 |     attribute :body, :string, allow_nil?: false
4017 |   end
4018 | 
4019 |   actions do
4020 |     defaults [:read, create: [:body], update: [:body]]
4021 |   end
4022 | end
4023 | 
4024 | defmodule SpecialContent do
4025 |   use Ash.Resource, data_layer: :embedded
4026 | 
4027 |   attributes do
4028 |     attribute :text, :string, allow_nil?: false
4029 |   end
4030 | 
4031 |   actions do
4032 |     defaults [:read, create: [:text], update: [:text]]
4033 |   end
4034 | end
4035 | 
4036 | defmodule Content do
4037 |   use Ash.Type.NewType,
4038 |     subtype_of: :union,
4039 |     constraints: [
4040 |       types: [
4041 |         normal: [
4042 |           type: NormalContent,
4043 |           tag: :type,
4044 |           tag_value: :normal
4045 |         ],
4046 |         special: [
4047 |           type: SpecialContent,
4048 |           tag: :type,
4049 |           tag_value: :special
4050 |         ]
4051 |       ]
4052 |     ]
4053 | end
4054 | ```
4055 | 
4056 | ## [](union-forms.html#determining-the-type-for-a-union-form)Determining the type for a union form
4057 | 
4058 | We track the type of the value in a hidden param called `_union_type`. You can use this to show a different form depending on the type of thing.
4059 | 
4060 | ## [](union-forms.html#changing-the-type-of-a-union-form)Changing the type of a union form
4061 | 
4062 | If you want to let the user *change* the union type, you would use [`AshPhoenix.Form.remove_form/3`](AshPhoenix.Form.html#remove_form/3) and [`AshPhoenix.Form.add_form/3`](AshPhoenix.Form.html#add_form/3). See the example below for the template, and here is an example event handler
4063 | 
4064 | ```
4065 | def handle_event("type-changed", %{"_target" => path} = params, socket) do
4066 |   new_type = get_in(params, path)
4067 |   # The last part of the path in this case is the field name
4068 |   path = :lists.droplast(path)
4069 | 
4070 |   form =
4071 |     socket.assigns.form
4072 |     |> AshPhoenix.Form.remove_form(path)
4073 |     |> AshPhoenix.Form.add_form(path, params: %{"_union_type" => new_type})
4074 | 
4075 |   {:noreply, assign(socket, :form, form)}
4076 | end
4077 | ```
4078 | 
4079 | ## [](union-forms.html#non-embedded-types)Non-embedded types
4080 | 
4081 | If one of your union values is a *non* embedded type, like `:integer`, it will still be a nested form, but you would access the single value with `<.input field={nested_form[:value]} .../>`
4082 | 
4083 | ## [](union-forms.html#example)Example
4084 | 
4085 | We might have a form like this:
4086 | 
4087 | ```
4088 | <.inputs_for :let={fc} field={@form[:content]}>
4089 |   <!-- Dropdown for setting the union type -->
4090 |   <.input
4091 |     field={fc[:_union_type]}
4092 |     phx-change="type-changed"
4093 |     type="select"
4094 |     options={[Normal: "normal", Special: "special"]}
4095 |   />
4096 | 
4097 |   <!-- switch on the union type to display a form -->
4098 |   <%= case fc.params["_union_type"] do %>
4099 |     <% "normal" -> %>
4100 |       <.input  type="text" field={fc[:body]} />
4101 |     <% "special" -> %>
4102 |       <.input type="text" field={fc[:text]} />
4103 |   <% end %>
4104 | </.inputs_for>
4105 | ```
4106 | 
4107 | [← Previous Page Get Started with Ash and Phoenix](getting-started-with-ash-and-phoenix.html)
4108 | 
4109 | [Next Page → Nested Forms](nested-forms.html)
4110 | 
4111 | [Hex Package](https://hex.pm/packages/ash_phoenix/2.1.14) [Hex Preview](https://preview.hex.pm/preview/ash_phoenix/2.1.14) ([current file](https://preview.hex.pm/preview/ash_phoenix/2.1.14/show/documentation/topics/union-forms.md)) Search HexDocs [Download ePub version](ash_phoenix.epub "ePub version")
4112 | 
4113 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.36.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
4114 | 
```
Page 7/10FirstPrevNextLast