### 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]} />
<.inputs_for :let={location} field={@form[:locations]}>
<.input field={location[:name]} />
""" 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]} />
<.inputs_for :let={location} field={@form[:locations]}>
<.input field={location[:name]} />
``` -------------------------------- ### 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.