What is AshPhoenix?
mainform_to_<name> functions for each function defined in your code interface. These functions return an %AshPhoenix.Form{} struct, which is initialized by passing all options through to AshPhoenix.Form.for_action/3.repository·main·Indexed 41 days ago
https://github.com/ash-project/ash_phoenixAn integration package that bridges the Ash Framework with the Phoenix Framework. It provides specialized data structures for forms (AshPhoenix.Form), LiveView helpers, multitenancy tools via subdomains, and code generators for building Ash-based web applications.
form_to_<name> functions for each function defined in your code interface. These functions return an %AshPhoenix.Form{} struct, which is initialized by passing all options through to AshPhoenix.Form.for_action/3.The ash_phoenix package provides several key tools for building Ash-powered Phoenix applications:
AshPhoenix.Form: A form data structure designed for using Ash resource actions within Phoenix forms.AshPhoenix.Form.Auto: Automatically determines nested form structures based on manage_relationship calls within an action.AshPhoenix.FilterForm: A data structure for building filter statements.AshPhoenix.FormData.Error: A protocol that enables rendering Ash errors within Phoenix forms.AshPhoenix.LiveView: Helpers for querying data and integrating changes within Phoenix LiveView.Phoenix.HTML.Safe implementations: Provides safe rendering for Ash.CiString, Ash.NotLoaded, and Decimal types.AshPhoenix.SubdomainPlug: A Plug used to determine a tenant based on the request's subdomain.AshPhoenix.LiveView.SubdomainHook: A hook for LiveView to determine a tenant using subdomains.mix ash_phoenix.gen.live: Generates LiveView modules.mix ash_phoenix.gen.html: Generates Phoenix controllers and views.By default, AshPhoenix ignores the args option in a resource's define block when building forms. To use positional arguments (which prevents certain values from being editable via the form), you must explicitly configure them in a forms block within the resource.
This is useful for passing values like user_id that should be part of the action but should not be user-editable in the UI.
# In your resource
resource MyApp.Accounts.User do
# ...
forms do
form :register_with_password, args: [:email]
end
end
# Usage in your domain/controller
MyApp.Accounts.form_to_register_with_password(email, ...opts)Nested or related forms are sets of inputs used for resource actions on related or embedded resources (e.g., a 'Business' form that includes multiple 'Location' forms).
AshPhoenix.Form can automatically infer these nested structures by introspecting actions that use change manage_relationship. When an action manages a relationship, the form allows you to use Phoenix's <.inputs_for> component to render inputs for those related resources. To disable this automatic behavior, set forms: [auto?: false] when creating the form.
# Example of an action that enables automatic nested form inference
```elixir
# on a `MyApp.Operations.Business` resource
create :create do
accept [:name]
argument :locations, {:array, :map}
change manage_relationship(:locations, type: :create)
end<%-- Using <.inputs_for> with the inferred 'locations' relationship --%
<.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]} />
</.inputs_for>
</.simple_form>AshPhoenix is an integration package designed to bridge the Ash Framework with the Phoenix Framework. It enables developers to leverage Phoenix's UI capabilities (such as Forms and LiveViews) while utilizing Ash's data management features.
Key integration points include:
AshPhoenix.Form: Tools for integrating Ash resources with Phoenix forms.AshPhoenix.LiveView: Tools for integrating Ash resources with Phoenix LiveViews.When building a form for a union type, you use inputs_for as you normally would. AshPhoenix handles the underlying complexity of the union automatically.
To track which member of the union is currently selected, AshPhoenix uses a hidden parameter named _union_type. You can use this parameter in your templates to conditionally render different input fields based on the active union type.
<.inputs_for :let={fc} field={@form[:content]}>
<!-- Use fc[:_union_type] to determine which fields to show -->
<%= case fc.params["_union_type"] do %>
<% "normal" -> %>
<.input type="text" field={fc[:body]} />
<% "special" -> %>
<.input type="text" field={fc[:text]} />
<% end %>
</.inputs_for>AshPhoenix is designed to be driven by your Ash resource configuration. To ensure forms and inputs work effectively, focus on defining well-structured resources. Specifically, ensure your resources have:
By making the resource the 'source of truth' for business logic, AshPhoenix can automatically derive much of the UI behavior.
If your code interface defines custom_input with a transform block, the generated form functions will honor these transformations for the function arguments, but will NOT apply them to the params map.
This means if an argument is transformed from a struct to an ID, the form will expect the ID for that argument, while the params map remains a raw key-value store of input data.
# In your domain
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
# Usage: The post struct is transformed to post_id in the form,
# but the params remain unchanged.
form = MyApp.Blog.form_to_create_with_post(
%MyApp.Blog.Post{id: "some-id"},
params: %{"text" => "Hello world"}
):integer), it is still treated as a nested form. However, because it is not a struct/resource, you must access the actual value using the :value key within the nested form. Use <.input field={nested_form[:value]} .../> to render the input.When using AshPhoenix.Form within a Phoenix LiveView, you should handle the result of AshPhoenix.Form.submit/2 to manage both successful submissions and validation errors.
{:ok, result}): Proceed with your success logic (e.g., redirecting or showing a success message).{:error, form}): Re-assign the returned form to the socket. This updated form contains the validation error messages, which can then be rendered in your template to show users what went wrong.# 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