brady131313

brady131313

How to create AshPhoenix form with Union field

I’m trying to create a form which has a list of nested items, each of which contain some union field. I have the following hierarchy: WorkoutTemplate has many SetGroupTemplate where SetGroupTemplate looks like the following module and both FixedReps and ClassicOverload are embedded ash resources

defmodule Weightroom.Template.SetGroupTemplate do
  @moduledoc false

  use Ash.Resource,
    domain: Weightroom.Template,
    data_layer: AshPostgres.DataLayer

  alias Weightroom.Template.Progression

  attributes do
    uuid_primary_key :id
    attribute :order, :integer, public?: true, allow_nil?: false

    attribute :progression, :union do
      public? true
      allow_nil? false

      constraints types: [
                    fixed_reps: [
                      type: Progression.FixedReps,
                      tag: :type,
                      tag_value: :fixed_reps
                    ],
                    classic_overload: [
                      type: Progression.ClassicOverload,
                      tag: :type,
                      tag_value: :classic_overload
                    ]
                  ]
    end
  end

  relationships do
    belongs_to :workout, Weightroom.Template.WorkoutTemplate do
      allow_nil? false
    end

    belongs_to :exercise, Weightroom.Journal.Exercise do
      allow_nil? false
      public? true
    end
  end

  postgres do
    table "set_group_templates"
    repo Weightroom.Repo

    references do
      reference :workout, on_delete: :delete
      reference :exercise, on_delete: :delete
    end

    custom_statements do
      statement :unique_order do
        up ~s|ALTER TABLE set_group_templates ADD CONSTRAINT set_group_templates_unique_order_index UNIQUE (workout_id, "order") DEFERRABLE INITIALLY DEFERRED|

        down "ALTER TABLE set_group_templates DROP CONSTRAINT set_group_templates_unique_order_index"
      end
    end
  end

  actions do
    defaults [:read, :destroy, create: :*]
  end

When I create the form and try to add a new SetGroupTemplate I get the following error

    template
    |> AshPhoenix.Form.for_update(:update, forms: [auto?: true])
    |> AshPhoenix.Form.add_form("form[set_groups]",
      params: %{
        "progression" => %{
          "_union_type" => "fixed_reps"
        }
      }
    )

** (RuntimeError) Got no "_union_type" parameter, and no union type had a tag & tag_value pair matching the params.

If you are adding a form, select a type using `params: %{"_union_type" => "type_name"}`, or if one
or more of your types is using a tag you can set that tag with `params: %{"tag" => "tag_value"}`.

Params:

%{}

Available types:

[
  classic_overload: [
    type: Weightroom.Template.Progression.ClassicOverload,
    constraints: [on_update: :update_on_match],
    tag: :type,
    tag_value: :classic_overload
  ],
  fixed_reps: [
    type: Weightroom.Template.Progression.FixedReps,
    constraints: [on_update: :update_on_match],
    tag: :type,
    tag_value: :fixed_reps
  ]
]

If I instead use “type” instead of “_union_type” as the key for the new progression I get the same error. Stepping through the code of ash authentication it looks like AshPhoenix.Form.Auto.determine_type/3 is called twice. The first time with the map of params and it doesn’t raise. The second time, params is empty and the function raises.

I saw there is a test for union forms here and I can’t see what I’m doing differently other than using an embedded resource for the union type instead of a simple type like string/int/etc. Any help understanding where I’m going wrong would be appreciated.

Marked As Solved

Neophen

Neophen

Ok figured it out,
the

defmodule Octafest.Pages.Block do
  use Ash.Type.NewType,
    subtype_of: :union,
    constraints: [
      types: [
        hero_simple: [
          type: HeroSimple,
          tag: :type,
          tag_value: :hero_simple
        ],
        hero_media: [
          type: HeroMedia,
          tag: :type,
          tag_value: :hero_media
        ]
      ]
    ]
end

has to be in a single file by itself
Same for all the others subtypes

Also Liked

Neophen

Neophen

Thanks Zach got most of the things working, UNIONS ARE AMAZING:

Now will try to swap two blocks :smiley:

brady131313

brady131313

@zachdaniel using main of ash_phoenix works as expected. I can pass the type of the union using both :_union_type and :tag and it works, thank you.

Neophen

Neophen

Works beautifully:

  def handle_event("add_block", %{"type" => type}, socket) do
    form =
      socket.assigns.form
      |> AshPhoenix.Form.add_form("form[blocks]", params: get_params(type))

    {:noreply, assign(socket, :form, form)}
  end

  defp get_params("hero_media" = type), do: %{
      "_union_type" => type,
      "image" => %{
        "url" => "",
        "alt" => "",
        "image_id" => "",
        "modifiers" => %{}
      }
    }

  defp get_params("hero_simple" = type), do: %{ "_union_type" => type }
  defp get_params(type), do: %{ "_union_type" => type }

Where Next?

Popular in Questions Top

tduccuong
Hi, is there any work on GUI with Elixir, that is similar to Electron/Javascript? My idea is to bundle Phoenix and BEAM into a single se...
New
johnnyicon
Hi all, I’ve just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I’m trying to use Postgres...
New
jaysoifer
Is there a way to rollback a specific migration and only that one (“skipping” all the other ones)? Would mix ecto.rollback -v 200809061...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
Emily
I have VueJS GUIs with the project generated using Webpack. I have Elixir modules that will need to be used by the VueJS GUIs. I forese...
New
belgoros
I’m not a pro in using Regex and can’t figure out why the following behaviour happens, especially if we take into account the difference ...
New
itssasanka
Hi all, Trying to get some more clarity over utc_datetime and naive_datetime for Ecto: The documentation above suggests that while ...
New
sergio_101
I am VERY much an elixir newbie. I have taken one elixir course and one phoenix course on Udemy. During that course, I saw the instructor...
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers’ Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New

Other popular topics Top

danschultzer
None of the current solutions worked well for me, so I went ahead and built a user management system from scratch. This project took far...
548 29377 241
New
malloryerik
Hi, this is for people who, like me, have had some friction using .html.heex templates in VSCode. The solution seems to be, in a hyphena...
New
albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod – where is this set? Thanks.
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
josevalim
Hi everyone, One of the features added to Elixir early on to help integration with Erlang code was the idea of overridable function defi...
New
baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
jason.o
In the code below, if the create action is not set to accept “extra_key” as an input, it errors out with a message shown above. Is there ...
New
marick
I had some trouble figuring out how to make many-to-many associations work. Once I got it working, I wrote a blog post. Because I’m a nov...
New
AstonJ
Seen any cool LiveView demos, sample apps or examples? Please post them here! :003:
New

We're in Beta

About us Mission Statement