### Example: Creating a Service with Locations
Source: https://github.com/ash-project/ash_phoenix/blob/main/documentation/topics/forms-for-relationships-between-existing-records.md
Demonstrates creating a 'Service' record and associating it with multiple existing 'Location' records using their IDs.
```elixir
iex> service = Ash.create!(Service, %{name: "Tuneup", location_ids: [location_1_id, location_2_id]}, load: [:locations])
%MyApp.Operations.Service{
id: 9,
name: "Tuneup",
location_relationships: [
%MyApp.Operations.ServiceLocation{ service_id: 9, location_id: 1, ... },
%MyApp.Operations.ServiceLocation{ service_id: 9, location_id: 2, ... }
],
locations: [
%MyApp.Operations.Location{ id: 1, name: "HQ", ... },
%MyApp.Operations.Location{ id: 2, name: "Downtown", ... }
],
...
}
```
--------------------------------
### Generate Form With Options
Source: https://github.com/ash-project/ash_phoenix/blob/main/documentation/dsls/DSL-AshPhoenix.md
This example demonstrates generating a form with the `params` option. The provided parameters are passed to the form builder.
```elixir
MyApp.Accounts.form_to_register_with_password(params: %{"email" => "placeholder@email"})
#=> %AshPhoenix.Form{}
```
--------------------------------
### Generate Phoenix LiveView for Ash Resource
Source: https://github.com/ash-project/ash_phoenix/blob/main/documentation/tutorials/getting-started-with-ash-and-phoenix.md
Use this command to generate a starting point for a Phoenix LiveView that interacts with your Ash resource. Remember that this is a starting point and may require further customization.
```bash
mix ash_phoenix.gen.live --domain Helpdesk.Support --resource Helpdesk.Support.Ticket
```
--------------------------------
### Customize Form Generation with Args
Source: https://github.com/ash-project/ash_phoenix/blob/main/documentation/dsls/DSL-AshPhoenix.md
This example demonstrates customizing the generated `form_to_create_student` function using the `form` DSL. It specifies custom arguments for the form.
```elixir
forms do
# customize the generated `form_to_create_student` function
form :create_student, args: [:school_id]
end
```
--------------------------------
### Generate Form Without Options
Source: https://github.com/ash-project/ash_phoenix/blob/main/documentation/dsls/DSL-AshPhoenix.md
This example shows how to generate a form using the default `form_to_register_with_password/2` function without any additional options.
```elixir
MyApp.Accounts.form_to_register_with_password()
#=> %AshPhoenix.Form{}
```
--------------------------------
### Add ash_phoenix to mix.exs
Source: https://github.com/ash-project/ash_phoenix/blob/main/README.md
Add `ash_phoenix` to your project's dependencies in `mix.exs` to install the package.
```elixir
{:ash_phoenix, "~> 2.3.23"}
```
--------------------------------
### Customize Form Generation with Args (Detailed)
Source: https://github.com/ash-project/ash_phoenix/blob/main/documentation/dsls/DSL-AshPhoenix.md
This example provides a more detailed view of customizing form generation, specifying the form name and its arguments. The `args` default to empty if not provided.
```elixir
# customize the generated `form_to_create_student` function
# args defaults to empty for form definitions
form :create_student, args: [:school_id]
```
--------------------------------
### Example of Submitted Service Parameters
Source: https://github.com/ash-project/ash_phoenix/blob/main/documentation/topics/forms-for-relationships-between-existing-records.md
This demonstrates the expected structure of parameters submitted for a service, including a list of location IDs. This format is compatible with `AshPhoenix.Form.submit/2` for managing relationships.
```elixir
%{"service" => %{"locations" => ["1", "2"], "name" => "Overhaul"}}
```
--------------------------------
### Generate Form for Update Action With Options
Source: https://github.com/ash-project/ash_phoenix/blob/main/documentation/dsls/DSL-AshPhoenix.md
This example shows generating an update form with the `params` option. The record and parameters are passed to the form builder.
```elixir
user = MyApp.Accounts.get_user!(id)
MyApp.Accounts.form_to_update_user(user, params: %{"email" => "placeholder@email"})
#=> %AshPhoenix.Form{}
```
--------------------------------
### Example: Updating a Service's Locations
Source: https://github.com/ash-project/ash_phoenix/blob/main/documentation/topics/forms-for-relationships-between-existing-records.md
Shows how to update an existing 'Service' record to change its associated 'Location's, in this case, removing one location.
```elixir
iex> Ash.update!(service, %{location_ids: [location_2_id]}, load: [:locations])
%MyApp.Operations.Service{
id: 9,
name: "Tuneup",
location_relationships: [
%MyApp.Operations.ServiceLocation{ service_id: 9, location_id: 2, ... }
],
locations: [
%MyApp.Operations.Location{ id: 2, name: "Downtown", ... }
],
...
}
```
--------------------------------
### Define Code Interface with Custom Inputs
Source: https://github.com/ash-project/ash_phoenix/blob/main/documentation/dsls/DSL-AshPhoenix.md
This example shows how to define a code interface with custom inputs and transformations. The `AshPhoenix` extension will honor these transformations for arguments but not for params.
```elixir
resource MyApp.Blog.Comment do
define :create_with_post do
action :create_with_post_id
args [:post]
custom_input :post, :struct do
constraints instance_of: MyApp.Blog.Post
transform to: :post_id, using: & &1.id
end
end
end
```
--------------------------------
### Render Select Input for Multiple Locations
Source: https://github.com/ash-project/ash_phoenix/blob/main/documentation/topics/forms-for-relationships-between-existing-records.md
This example shows how to render a select input for a multiple-choice field, mapping location names to their IDs for the form. It assumes `@locations` are already assigned to the socket.
```html
<.input
field={@form[:location_ids]}
type="select"
multiple
label="Locations"
options={Enum.map(@locations, &{&1.name, &1.id})}
/>
```
--------------------------------
### Using `inputs_for` with Inferred Nested Forms
Source: https://github.com/ash-project/ash_phoenix/blob/main/documentation/topics/nested-forms.md
When nested forms are inferred, you can use Phoenix's `<.inputs_for>` component to render inputs for the related resources. This example shows how to render inputs for `locations` within a form.
```heex
<.simple_form for={@form} phx-change="validate" phx-submit="submit">
<.input field={@form[:email]} />
<.inputs_for :let={location} field={@form[:locations]}>
<.input field={location[:name]} />
```
--------------------------------
### Inferring Nested Forms from Action
Source: https://github.com/ash-project/ash_phoenix/blob/main/documentation/topics/nested-forms.md
AshPhoenix.Form automatically infers nested forms from actions using `manage_relationship`. This example shows an action that accepts a list of locations.
```elixir
create :create do
accept [:name]
argument :locations, {:array, :map}
change manage_relationship(:locations, type: :create)
end
```
--------------------------------
### Generate Form for Update Action Without Options
Source: https://github.com/ash-project/ash_phoenix/blob/main/documentation/dsls/DSL-AshPhoenix.md
For update actions, the record being updated is required as the first argument. This example shows generating an update form without additional options.
```elixir
user = MyApp.Accounts.get_user!(id)
MyApp.Accounts.form_to_update_user(user)
#=> %AshPhoenix.Form{}
```
--------------------------------
### Adding Nested Forms with `_add_*` Checkbox
Source: https://github.com/ash-project/ash_phoenix/blob/main/documentation/topics/nested-forms.md
Use a hidden checkbox with a specific name format (`form[_add_related_resource]=value`) to dynamically add new nested forms. The `value` can be 'start', 'end', or an index.
```heex
<.simple_form for={@form} phx-change="validate" phx-submit="submit">
<.input field={@form[:email]} />
<.inputs_for :let={location} field={@form[:locations]}>
<.input field={location[:name]} />
```
--------------------------------
### Create and Update Forms with AshPhoenix.Form
Source: https://github.com/ash-project/ash_phoenix/blob/main/usage-rules/form_integration.md
Use `AshPhoenix.Form.for_create/3` to generate a form for creating a new resource, and `AshPhoenix.Form.for_update/3` for updating an existing resource. You can also provide initial parameters.
```elixir
form = AshPhoenix.Form.for_create(MyApp.Blog.Post, :create) |> to_form()
# For updating an existing resource
post = MyApp.Blog.get_post!(post_id)
form = AshPhoenix.Form.for_update(post, :update) |> to_form()
# Form with initial value
form = AshPhoenix.Form.for_create(MyApp.Blog.Post, :create,
params: %{title: "Draft Title"}
) |> to_form()
```
--------------------------------
### Handling Form Submission in LiveView
Source: https://github.com/ash-project/ash_phoenix/blob/main/usage-rules/form_integration.md
Use `AshPhoenix.Form.submit/2` to submit the form data. Handle successful submissions by navigating to a new page or displaying a success message. Handle errors by re-assigning the form to the socket.
```elixir
def handle_event("submit", %{"form" => params}, socket) do
case AshPhoenix.Form.submit(socket.assigns.form, params: params) do
{:ok, post} ->
socket =
socket
|> put_flash(:info, "Post created successfully")
|> push_navigate(to: ~p"/posts/#{post.id}")
{:noreply, socket}
{:error, form} ->
{:noreply, assign(socket, :form, form)}
end
end
```
--------------------------------
### Generating Forms from Resource Actions
Source: https://github.com/ash-project/ash_phoenix/blob/main/usage-rules/form_integration.md
After adding the `AshPhoenix` extension, you can create forms for defined actions using the generated `form_to_*` functions. By default, `args` in `define` are ignored for forms.
```elixir
# in MyApp.Accounts
resources do
resource MyApp.Accounts.User do
define :register_with_password, args: [:email, :password]
end
end
MyApp.Accounts.form_to_register_with_password(...opts)
```
--------------------------------
### Custom Create Action for Service with Relationship Management
Source: https://github.com/ash-project/ash_phoenix/blob/main/documentation/topics/forms-for-relationships-between-existing-records.md
Declares a custom 'create' action for the 'Service' resource. It accepts 'location_ids' and uses `manage_relationship` to add or remove 'ServiceLocation' entries.
```elixir
create :create do
accept [:name]
primary? true
argument :location_ids, {:array, :integer}, allow_nil?: true
change manage_relationship(:location_ids, :locations, type: :append_and_remove)
end
```
--------------------------------
### Define Form for Service Creation or Update
Source: https://github.com/ash-project/ash_phoenix/blob/main/documentation/topics/forms-for-relationships-between-existing-records.md
This function assigns a form to the socket, either for creating a new service or updating an existing one. It prepares parameters for the form, ensuring location IDs are handled correctly.
```elixir
defp assign_form(%{assigns: %{service: service}} = socket) do
form =
if service do
service
|> Ash.load!([:locations, :location_ids])
|> AshPhoenix.Form.for_update(:update, as: "service", prepare_params: &prepare_params/2)
else
AshPhoenix.Form.for_create(MyApp.Operations.Service, :create, as: "service")
end
assign(socket, form: to_form(form))
end
defp prepare_params(params, :validate) do
Map.put_new(params, "location_ids", [])
end
```
--------------------------------
### AshPhoenix Extension for Domain Resources
Source: https://github.com/ash-project/ash_phoenix/blob/main/usage-rules/form_integration.md
Add the `AshPhoenix` extension to your domain to generate `form_to_*` functions for resource actions. This simplifies form creation by directly using resource definitions.
```elixir
use Ash.Domain,
extensions: [AshPhoenix]
```
--------------------------------
### Handle Adding Nested Form in LiveView
Source: https://github.com/ash-project/ash_phoenix/blob/main/usage-rules/nested_forms.md
Implement the `handle_event` for 'add-form' in your LiveView. Use `AshPhoenix.Form.add_form/3` to dynamically add a new nested form to the socket's form assignment.
```elixir
def handle_event("add-form", %{"path" => path}, socket) do
form = AshPhoenix.Form.add_form(socket.assigns.form, path)
{:noreply, assign(socket, :form, form)}
end
```
--------------------------------
### LiveView with Nested Form
Source: https://github.com/ash-project/ash_phoenix/blob/main/documentation/topics/nested-forms.md
This LiveView demonstrates a complete form with nested inputs for locations, including features for adding, removing, and sorting items. It handles form validation and submission.
```elixir
defmodule MyApp.MyForm do
use MyAppWeb, :live_view
def render(assigns) do
~H"""
<.simple_form for={@form} phx-change="validate" phx-submit="submit">
<.input field={@form[:email]} />
"""
end
def mount(_params, _session, socket) do
{:ok, assign(socket, form: MyApp.Operations.form_to_create_business())}
end
def handle_event("validate", %{"form" => params}, socket) do
{:noreply, assign(socket, :form, AshPhoenix.Form.validate(socket.assigns.form, params))}
end
def handle_event("submit", %{"form" => params}, socket) do
case AshPhoenix.Form.submit(socket.assigns.form, params: params) do
{:ok, business} ->
socket =
socket
|> put_flash(:success, "Business created successfully")
|> push_navigate(to: ~p"/businesses/#{business.id}")
{:noreply, socket}
{:error, form} ->
{:noreply, assign(socket, :form, form)}
end
end
end
```
--------------------------------
### Add Nested Form with Button
Source: https://github.com/ash-project/ash_phoenix/blob/main/usage-rules/nested_forms.md
Use a button with `phx-click` to trigger an event that adds a new nested form. The `phx-value-path` should point to the nested attribute in the form.
```heex
<.button type="button" phx-click="add-form" phx-value-path={@form.name <> "[locations]"}>
<.icon name="hero-plus" />
```
--------------------------------
### Usage of Transformed Input in Form
Source: https://github.com/ash-project/ash_phoenix/blob/main/documentation/dsls/DSL-AshPhoenix.md
Demonstrates how a custom input transformation is applied when using the generated form function. The `post` struct is transformed to `post_id`, while `params` remain unchanged.
```elixir
# Usage - the post argument will be transformed
form = MyApp.Blog.form_to_create_with_post(
%MyApp.Blog.Post{id: "some-id"},
params: %{"text" => "Hello world"}
)
# The post struct gets transformed to post_id in the form
# The params remain unchanged
```
--------------------------------
### Add Nested Form with Button Click
Source: https://github.com/ash-project/ash_phoenix/blob/main/documentation/topics/nested-forms.md
Use a button with `phx-click` to trigger adding a new nested form section. The `phx-value-path` is crucial for correctly identifying where to add the new form, especially in deeply nested structures.
```heex
<.simple_form for={@form} phx-change="validate" phx-submit="submit">
<.input field={@form[:email]} />
<.inputs_for :let={location} field={@form[:locations]}>
<.input field={location[:name]} />
<.button type="button" phx-click="add-form" phx-value-path={@form.name <> "[locations]"}>
<.icon name="hero-plus" />
```
--------------------------------
### Define Location Resource
Source: https://github.com/ash-project/ash_phoenix/blob/main/documentation/topics/forms-for-relationships-between-existing-records.md
A basic Ash resource for a 'Location' entity with an integer primary key and a string name attribute.
```elixir
defmodule MyApp.Operations.Location do
use Ash.Resource,
otp_app: :my_app,
domain: MyApp.Operations,
data_layer: AshPostgres.DataLayer
...
attributes do
integer_primary_key :id
attribute :name, :string do
allow_nil? false
end
end
end
```
--------------------------------
### Custom Update Action for Service with Relationship Management
Source: https://github.com/ash-project/ash_phoenix/blob/main/documentation/topics/forms-for-relationships-between-existing-records.md
Declares a custom 'update' action for the 'Service' resource. It accepts 'location_ids' and uses `manage_relationship` with `type: :append_and_remove` for dynamic relationship updates.
```elixir
update :update do
accept [:name]
primary? true
argument :location_ids, {:array, :integer}, allow_nil?: true
require_atomic? false
change manage_relationship(:location_ids, :locations, type: :append_and_remove)
end
```
--------------------------------
### Define Service Resource with Location Relationship
Source: https://github.com/ash-project/ash_phoenix/blob/main/documentation/topics/forms-for-relationships-between-existing-records.md
An Ash resource for 'Service' with a many-to-many relationship to 'Location' through 'ServiceLocation'. Includes a list aggregate for 'location_ids'.
```elixir
defmodule MyApp.Operations.Service do
use Ash.Resource,
otp_app: :my_app,
domain: MyApp.Operations,
data_layer: AshPostgres.DataLayer
...
relationships do
has_many :location_relationships, MyApp.Operations.ServiceLocation do
destination_attribute :service_id
end
many_to_many :locations, MyApp.Operations.Location do
join_relationship :location_relationships
source_attribute_on_join_resource :service_id
destination_attribute_on_join_resource :location_id
end
end
aggregates do
list :location_ids, :locations, :id
end
end
```
--------------------------------
### Dynamic Union Form with Type Switching
Source: https://github.com/ash-project/ash_phoenix/blob/main/documentation/topics/union-forms.md
A HEEx template demonstrating a dynamic form for a union type. It includes a dropdown to select the union type and conditionally renders input fields based on the selected type.
```heex
<.inputs_for :let={fc} field={@form[:content]}>
<.input
field={fc[:_union_type]}
phx-change="type-changed"
type="select"
options={[Normal: "normal", Special: "special"]}
/>
<%= case fc.params["_union_type"] do %>
<% "normal" -> %>
<.input type="text" field={fc[:body]} />
<% "special" -> %>
<.input type="text" field={fc[:text]} />
<% end %>
```
--------------------------------
### View All Raw Errors in Ash Phoenix Form
Source: https://github.com/ash-project/ash_phoenix/blob/main/usage-rules/debugging_form_submissions.md
Use `AshPhoenix.Form.raw_errors/2` with `for_path: :all` to see all errors from a failed form submission, including those not tied to specific fields. This is useful for identifying and resolving unexpected errors.
```elixir
AshPhoenix.Form.raw_errors(form, for_path: :all)
```
--------------------------------
### Handling Form Validation in LiveView
Source: https://github.com/ash-project/ash_phoenix/blob/main/usage-rules/form_integration.md
Use `AshPhoenix.Form.validate/3` in your LiveView's `handle_event/3` function to validate form input against the current form state. Assign the validated form back to the socket.
```elixir
def handle_event("validate", %{"form" => params}, socket) do
form = AshPhoenix.Form.validate(socket.assigns.form, params)
{:noreply, assign(socket, :form, form)}
end
```
--------------------------------
### Loading Relationships for Updates
Source: https://github.com/ash-project/ash_phoenix/blob/main/documentation/topics/nested-forms.md
When updating nested items, ensure relationships are loaded. AshPhoenix.Form will render a form for each existing, loaded related item.
```elixir
business = Ash.load!(business, :locations)
form = AshPhoenix.Form.for_update(business, :update)
```
--------------------------------
### Render Nested Forms with Sortable Input
Source: https://github.com/ash-project/ash_phoenix/blob/main/documentation/topics/nested-forms.md
Use the `_sort_*` hidden input within each nested form to allow `AshPhoenix.Form` to correctly reorder items based on front-end drag-and-drop interactions. The `phx-hook` attribute on the parent element enables the JavaScript sorting.
```heex
<.simple_form for={@form} phx-change="validate" phx-submit="submit">
<.input field={@form[:email]} />
```
--------------------------------
### Render Union Form Inputs with HEEx
Source: https://github.com/ash-project/ash_phoenix/blob/main/usage-rules/union_forms.md
Use this HEEx snippet to render form inputs for a union type. It includes a select input for choosing the union type and conditional inputs based on the selected type. Attach a `phx-change` event to the union type selector to handle type changes.
```heex
<.inputs_for :let={fc} field={@form[:content]}>
<.input
field={fc[:_union_type]}
phx-change="type-changed"
type="select"
options={[Normal: "normal", Special: "special"]}
/>
<%= case fc.params["_union_type"] do %>
<% "normal" -> %>
<.input type="text" field={fc[:body]} />
<% "special" -> %>
<.input type="text" field={fc[:text]} />
<% end %>
```
--------------------------------
### Manually Defining Nested Forms
Source: https://github.com/ash-project/ash_phoenix/blob/main/documentation/topics/nested-forms.md
You can manually specify nested form configurations using the `forms` option when creating a form. This is an escape hatch for customization.
```elixir
AshPhoenix.Form.for_create(
MyApp.Operations.Business,
:create,
forms: [
locations: [
type: :list,
resource: MyApp.Operations.Location,
create_action: :create
]
]
)
```
--------------------------------
### View Field-Specific Errors in Ash Phoenix Form
Source: https://github.com/ash-project/ash_phoenix/blob/main/usage-rules/debugging_form_submissions.md
Use `AshPhoenix.Form.errors/2` with `for_path: :all` to view errors that implement the `AshPhoenix.FormData.Error` protocol and are associated with specific fields. This is helpful for debugging issues displayed directly within form inputs.
```elixir
AshPhoenix.Form.errors(form, for_path: :all)
```
--------------------------------
### Configuring Positional Arguments for Forms
Source: https://github.com/ash-project/ash_phoenix/blob/main/usage-rules/form_integration.md
Configure positional arguments for forms within the `forms` section of your resource definition. This is useful for arguments that should not be directly set by the form.
```elixir
forms do
form :register_with_password, args: [:email]
end
MyApp.Accounts.register_with_password(email, ...)
```
--------------------------------
### Handle Form Submission Errors in LiveView
Source: https://github.com/ash-project/ash_phoenix/blob/main/usage-rules/error_handling.md
This snippet shows how to handle the success and error cases of a form submission within a Phoenix LiveView. It specifically demonstrates displaying validation errors by re-assigning the form.
```elixir
# In your LiveView
def handle_event("submit", %{"form" => params}, socket) do
case AshPhoenix.Form.submit(socket.assigns.form, params: params) do
{:ok, post} ->
# Success path
{:noreply, success_path(socket, post)}
{:error, form} ->
# Show validation errors
{:noreply, assign(socket, form: form)}
end
end
```
--------------------------------
### Remove Nested Form with Button Click
Source: https://github.com/ash-project/ash_phoenix/blob/main/documentation/topics/nested-forms.md
Implement manual removal of nested forms using a button and `phx-click`. The `phx-value-path` attribute on the button should contain the specific path to the form section to be removed.
```heex
<.simple_form for={@form} phx-change="validate" phx-submit="submit">
<.input field={@form[:email]} />
<.inputs_for :let={location} field={@form[:locations]}>
<.input field={location[:name]} />
<.button type="button" phx-click="remove-form" phx-value-path={location.name}>
<.icon name="hero-x-mark" />
```
--------------------------------
### Handle Removing Nested Form in LiveView
Source: https://github.com/ash-project/ash_phoenix/blob/main/usage-rules/nested_forms.md
Implement the `handle_event` for 'remove-form' in your LiveView. Use `AshPhoenix.Form.remove_form/3` to dynamically remove a nested form from the socket's form assignment.
```elixir
def handle_event("remove-form", %{"path" => path}, socket) do
form = AshPhoenix.Form.remove_form(socket.assigns.form, path)
{:noreply, assign(socket, :form, form)}
end
```
--------------------------------
### Handle Union Type Change Event in LiveView
Source: https://github.com/ash-project/ash_phoenix/blob/main/usage-rules/union_forms.md
Implement this LiveView event handler to manage changes to the union type selection. It removes the old form for the union type and adds a new one with the updated type, ensuring the form state is correctly managed.
```elixir
def handle_event("type-changed", %{"_target" => path} = params, socket) do
new_type = get_in(params, path)
path = :lists.droplast(path)
form =
socket.assigns.form
|> AshPhoenix.Form.remove_form(path)
|> AshPhoenix.Form.add_form(path, params: %{"_union_type" => new_type})
{:noreply, assign(socket, :form, form)}
end
```
--------------------------------
### Render Nested Forms in Phoenix Template
Source: https://github.com/ash-project/ash_phoenix/blob/main/usage-rules/nested_forms.md
Render nested forms in your Phoenix templates using `<.inputs_for>`. This iterates over the nested form fields, allowing you to render inputs for each nested item.
```heex
<.simple_form for={@form} phx-change="validate" phx-submit="submit">
<.input field={@form[:name]} />
<.inputs_for :let={location} field={@form[:locations]}>
<.input field={location[:name]} />
```
--------------------------------
### Move Specific Form Up or Down
Source: https://github.com/ash-project/ash_phoenix/blob/main/documentation/topics/nested-forms.md
Implement event handlers for `"move-up"` and `"move-down"` to reorder a specific nested form. The `AshPhoenix.Form.sort_forms/3` function is used with `:decrement` or `:increment` to adjust the order.
```elixir
def handle_event("move-up", %{"path" => form_to_move}, socket) do
form = AshPhoenix.Form.sort_forms(socket, form_to_move, :decrement)
{:noreply, assign(socket, form: form)}
end
def handle_event("move-down", %{"path" => form_to_move}, socket) do
form = AshPhoenix.Form.sort_forms(socket, form_to_move, :increment)
{:noreply, assign(socket, form: form)}
end
```
--------------------------------
### Handle Adding Nested Form in Elixir
Source: https://github.com/ash-project/ash_phoenix/blob/main/documentation/topics/nested-forms.md
The `handle_event` function processes the 'add-form' event, using `AshPhoenix.Form.add_form` to dynamically add a new nested form section to the existing form. It allows passing initial parameters for the new form section.
```elixir
def handle_event("add-form", %{"path" => path}, socket) do
form = AshPhoenix.Form.add_form(socket.assigns.form, path, params: %{
address: "Put your address here!"
})
{:noreply, assign(socket, :form, form)}
end
```
--------------------------------
### Sort Nested Forms with Full Index List
Source: https://github.com/ash-project/ash_phoenix/blob/main/documentation/topics/nested-forms.md
Handle a `"update-sorting"` event to reorder nested forms by providing a list of indices in the desired order. This function updates the form socket assigns with the sorted form.
```elixir
def handle_event("update-sorting", %{"path" => path, "indices" => indices}, socket) do
form = AshPhoenix.Form.sort_forms(socket, path, indices)
{:noreply, assign(socket, form: form)}
end
```
--------------------------------
### Defining Union Types in Ash
Source: https://github.com/ash-project/ash_phoenix/blob/main/documentation/topics/union-forms.md
Defines `NormalContent`, `SpecialContent`, and a `Content` union type using Ash. This sets up the structure for different content types within a single union.
```elixir
defmodule NormalContent do
use Ash.Resource, data_layer: :embedded
attributes do
attribute :body, :string, allow_nil?: false, public?: true
end
actions do
defaults [:read, create: [:body], update: [:body]]
end
end
defmodule SpecialContent do
use Ash.Resource, data_layer: :embedded
attributes do
attribute :text, :string, allow_nil?: false, public?: true
end
actions do
defaults [:read, create: [:text], update: [:text]]
end
end
defmodule Content do
use Ash.Type.NewType,
subtype_of: :union,
constraints: [
types: [
normal: [
type: :struct,
constraints: [instance_of: NormalContent],
tag: :type,
tag_value: :normal
],
special: [
type: :struct,
constraints: [instance_of: SpecialContent],
tag: :type,
tag_value: :special
]
]
]
end
```
--------------------------------
### Define ServiceLocation Joining Resource
Source: https://github.com/ash-project/ash_phoenix/blob/main/documentation/topics/forms-for-relationships-between-existing-records.md
The joining resource 'ServiceLocation' that manages the many-to-many relationship between 'Service' and 'Location'. It has default actions and declares its belongs_to relationships.
```elixir
defmodule MyApp.Operations.ServiceLocation do
use Ash.Resource,
otp_app: :my_app,
domain: MyApp.Operations,
data_layer: AshPostgres.DataLayer
...
actions do
defaults [:create, :read, :update, :destroy]
default_accept [:service_id, :location_id]
end
relationships do
belongs_to :service, MyApp.Operations.Service do
attribute_type :integer
allow_nil? false
primary_key? true
end
belongs_to :location, MyApp.Operations.Location do
attribute_type :integer
allow_nil? false
primary_key? true
end
end
end
```
--------------------------------
### Remove Nested Form with Button
Source: https://github.com/ash-project/ash_phoenix/blob/main/usage-rules/nested_forms.md
Use a button with `phx-click` to trigger an event that removes a nested form. The `phx-value-path` should be the name of the specific nested form to remove.
```heex
<.button type="button" phx-click="remove-form" phx-value-path={location.name}>
<.icon name="hero-x-mark" />
```
--------------------------------
### Define Nested Form in Ash Resource
Source: https://github.com/ash-project/ash_phoenix/blob/main/usage-rules/nested_forms.md
Use `manage_relationship` in your Ash resource's create action to automatically handle nested forms. This requires defining the nested resource's fields in the action's arguments.
```elixir
create :create do
accept [:name]
argument :locations, {:array, :map}
change manage_relationship(:locations, type: :create)
end
```
--------------------------------
### Configure Sortable.js Hook
Source: https://github.com/ash-project/ash_phoenix/blob/main/documentation/topics/nested-forms.md
Set up a `Sortable` hook in your JavaScript to enable drag-and-drop functionality for nested form elements. This hook triggers an input event on the closest form when the sorting ends.
```javascript
import Sortable from "sortablejs"
export const Sortable = {
mounted() {
new Sortable(this.el, {
animation: 150,
draggable: '[data-sortable="true"]',
ghostClass: "bg-yellow-100",
dragClass: "shadow-2xl",
onEnd: (evt) => {
this.el.closest("form").querySelector("input").dispatchEvent(new Event("input", {bubbles: true}))
}
})
}
}
let Hooks = {}
Hooks.Sortable = Sortable
```
--------------------------------
### Handling Union Type Changes in Phoenix
Source: https://github.com/ash-project/ash_phoenix/blob/main/documentation/topics/union-forms.md
An event handler for Phoenix LiveView that updates the form when a user changes the union type. It removes the old form and adds a new one based on the selected type.
```elixir
def handle_event("type-changed", %{"_target" => path} = params, socket) do
new_type = get_in(params, path)
# The last part of the path in this case is the field name
path = :lists.droplast(path)
form =
socket.assigns.form
|> AshPhoenix.Form.remove_form(path)
|> AshPhoenix.Form.add_form(path, params: %{"_union_type" => new_type})
{:noreply, assign(socket, :form, form)}
end
```
--------------------------------
### Handle Removing Nested Form in Elixir
Source: https://github.com/ash-project/ash_phoenix/blob/main/documentation/topics/nested-forms.md
The `handle_event` function processes the 'remove-form' event by calling `AshPhoenix.Form.remove_form` with the provided path to remove the specified nested form section from the main form.
```elixir
def handle_event("remove-form", %{"path" => path}, socket) do
form = AshPhoenix.Form.remove_form(socket.assigns.form, path)
{:noreply, assign(socket, :form, form)}
end
```
--------------------------------
### Remove Nested Form with Checkbox
Source: https://github.com/ash-project/ash_phoenix/blob/main/documentation/topics/nested-forms.md
Utilize a hidden checkbox with a `_drop_` prefix in its name to mark nested form sections for removal. The server receives the indices of the forms to be dropped, which are then automatically handled during validation.
```heex
<.simple_form for={@form} phx-change="validate" phx-submit="submit">
<.input field={@form[:email]} />
<.inputs_for :let={location} field={@form[:locations]}>
<.input field={location[:name]} />
```
=== COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.