View Source Union Forms
When building a form for a union, you use inputs_for
as normal, but a few things are done for you under the hood.
Lets take this example union:
defmodule NormalContent do
use Ash.Resource, data_layer: :embedded
attributes do
attribute :body, :string, allow_nil?: false
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
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: NormalContent,
tag: :type,
tag_value: :normal
],
special: [
type: SpecialContent,
tag: :type,
tag_value: :special
]
]
]
end
Determining the type for a union form
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.
Changing the type of a union form
If you want to let the user change the union type, you would use AshPhoenix.Form.remove_form/3
and AshPhoenix.Form.add_form/3
. See the example below for the template, and here is an example event handler
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
Non-embedded types
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]} .../>
Example
We might have a form like this:
<.inputs_for :let={fc} field={@form[:content]}>
<!-- Dropdown for setting the union type -->
<.input
field={fc[:_union_type}
phx-change="type-changed"
type="select"
options={[Normal: "normal", Special: "special"]}
/>
<!-- switch on the union type to display a form -->
<%= case fc.params["_union_type"] do %>
<% "normal" -> %>
<.input type="text" field={fc[:body]} />
<% "special" -> %>
<.input type="text" field={fc[:text]} />
<% end %>
</.inputs_for>