Hi,
I’m trying out the Ash Framework (loving it so far!) and am facing some issues with defining forms that have a select option for Enums. The setup is quite simple: it’s a trivial resource definition, similar to the one on AshHq, and a barebone LiveView defining a AshPhoenix.Form
. Here’s the code:
Resource definition:
defmodule MyApp.Products.Product do
use Ash.Resource, data_layer: AshPostgres.DataLayer
postgres do
table "products"
repo MyApp.Repo
end
code_interface do
define_for MyApp.Products
define :create, action: :create
define :read_all, action: :read
define :update, action: :update
define :destroy, action: :destroy
define :get_by_id, args: [:id], action: :by_id
end
actions do
defaults [:create, :read, :update, :destroy]
read :by_id do
argument :id, :uuid, allow_nil?: false
# Tells us we expect this action to return a single result
get? true
filter expr(id == ^arg(:id))
end
end
attributes do
uuid_primary_key :id
attribute :sku, :string
attribute :title, :string do
allow_nil? false
end
attribute :description, :string
attribute :price, :integer
attribute :currency, :atom do
constraints one_of: MyApp.Currencies
end
create_timestamp :created_at
update_timestamp :updated_at
end
end
Enum definition:
defmodule MyApp.Currencies do
use Ash.Type.Enum, values: [:usd, :eur, :gbp]
end
And here’s the LiveComponent code that defines the form:
defmodule MyAppWeb.Live.Products.FormComponent do
use MyAppWeb, :live_component
alias MyApp.Products.Product
require Logger
@impl true
def render(assigns) do
~H"""
<div>
<.header>
<%= @title %>
<:subtitle>Use this form to manage product records in your database.</:subtitle>
</.header>
<.simple_form
for={@form}
id="product-form"
phx-target={@myself}
phx-change="validate"
phx-submit="submit"
>
<.input type="text" field={@form[:title]} label="Enter a title" />
<.input type="text" field={@form[:sku]} label="Enter a SKU" />
<.input type="number" min="0" field={@form[:price]} label="Enter a price" />
<.input
type="select"
field={@form[:currency]}
label="Select the currency"
options={[nil] ++ MyApp.Currencies.values()}
/>
<:actions>
<.button phx-disable-with="Saving...">Save Product</.button>
</:actions>
</.simple_form>
</div>
"""
end
@impl true
def update(%{product: product} = assigns, socket) do
form = product |> change_product(assigns.action) |> to_form()
{:ok,
socket
|> assign(assigns)
|> assign(:form, form)}
end
@impl true
def handle_event("validate", %{"form" => params}, socket) do
form = AshPhoenix.Form.validate(socket.assigns.form, params)
{:noreply, assign(socket, form: form)}
end
def handle_event("submit", %{"form" => params}, socket) do
case AshPhoenix.Form.submit(socket.assigns.form, params: params) do
{:ok, product} ->
notify_parent({:saved, product})
{:noreply,
socket
|> put_flash(:info, "Successfully saved the order!")
|> push_patch(to: socket.assigns.patch)}
{:error, form} ->
Logger.error("Form errors: #{inspect(form.errors)}")
{:noreply, assign(socket, form: form)}
end
end
defp change_product(_product, :new) do
AshPhoenix.Form.for_create(Product, :create, api: MyApp.Products)
end
defp change_product(product, :edit) do
AshPhoenix.Form.for_update(product, :update, api: MyApp.Products)
end
defp notify_parent(msg), do: send(self(), {__MODULE__, msg})
end
All form fields on the form work, except for :currencies
. More specifically, as soon as I select an option from the dropdown, the GenServer crashes with the following error:
** (Protocol.UndefinedError) protocol Enumerable not implemented for MyApp.Currencies of type Atom
(elixir 1.15.2) lib/enum.ex:1: Enumerable.impl_for!/1
(elixir 1.15.2) lib/enum.ex:194: Enumerable.member?/2
(elixir 1.15.2) lib/enum.ex:1999: Enum.member?/2
(ash 2.17.4) lib/ash/type/atom.ex:38: anonymous fn/3 in Ash.Type.Atom.apply_constraints/2
(elixir 1.15.2) lib/enum.ex:2510: Enum."-reduce/3-lists^foldl/2-0-"/3
(ash 2.17.4) lib/ash/type/atom.ex:36: Ash.Type.Atom.apply_constraints/2
(ash 2.17.4) lib/ash/type/type.ex:641: Ash.Type.apply_constraints/3
(ash 2.17.4) lib/ash/changeset/changeset.ex:3487: Ash.Changeset.change_attribute/3
(stdlib 5.0.2) maps.erl:416: :maps.fold_1/4
(ash 2.17.4) lib/ash/changeset/changeset.ex:934: Ash.Changeset.handle_params/4
(ash 2.17.4) lib/ash/changeset/changeset.ex:886: Ash.Changeset.do_for_action/4
(ash_phoenix 1.2.23) lib/ash_phoenix/form/form.ex:1057: AshPhoenix.Form.validate/3
(ash_phoenix 1.2.23) lib/ash_phoenix/form/form.ex:948: AshPhoenix.Form.validate/3
(my_app 0.1.0) lib/my_app_web/live/product_live/form_component.ex:56: MyAppWeb.Live.Products.FormComponent.handle_event/3
I suppose the solution is trivial, but I just can’t quite understand what’s the issue. Would appreciate any pointers!