mathieuprog / polymorphic_embed

Polymorphic embeds in Ecto
Apache License 2.0
341 stars 63 forks source link

PolymorphicEmbed.HTML.Form.polymorphic_embed_inputs_for doesn't add arrray index to input field's name for a list of embeds #75

Closed maxmarcon closed 6 months ago

maxmarcon commented 1 year ago

When I use Phoenix.HTML.Form.inputs_for/2 on a list of embeds (i.e. defined using embeds_many), the name of the fields for the embeds generated with Phoenix.HTML.Form.input_name/2 includes an index ([0], [1] etc.), which is necessary to submit the list of the embeds in the form as a list and not as a single embed.

When I use PolymorphicEmbed.HTML.Form.polymorphic_embed_inputs_for/2 however, no index is generated. See example below:

Mix.install([{:ecto, "~> 3.9.4"}, {:polymorphic_embed, "~> 3.0.5"}, {:phoenix_html, " ~> 3.2"}, {:phoenix_ecto, "~> 4.4"}])

defmodule ChildSchema do
  use Ecto.Schema
  import Ecto.Changeset

  embedded_schema do
    field(:name, :string)
    field(:age, :integer)
  end

  def changeset(source \\ %__MODULE__{}, changes) do
    cast(source, changes, [:name, :age])
    |> validate_required([:name])
  end
end

defmodule ParentSchema do
  use Ecto.Schema
  import Ecto.Changeset
  import PolymorphicEmbed

  embedded_schema do
    embeds_many(:children, ChildSchema)

    polymorphic_embeds_many(:polymorphic_children,
      types: [
        child_schema: ChildSchema
      ],
      on_replace: :delete,
      on_type_not_found: :raise
    )
  end

  def changeset(source \\ %__MODULE__{}, changes) do
    cast(source, changes, [])
    |> cast_embed(:children)
    |> cast_polymorphic_embed(:polymorphic_children)
  end
end

children_params = [%{name: "Tic", age: 10}, %{name: "Tac", age: 12}]

form = ParentSchema.changeset(%{
  children: children_params,
  polymorphic_children: children_params |> Enum.map(&Map.put(&1, :__type__, :child_schema)),
})
|> Phoenix.HTML.FormData.to_form([])

for f <- Phoenix.HTML.Form.inputs_for(form, :children) do
  Phoenix.HTML.Form.input_name(f, :name) |> IO.inspect(label: "input_name(f, :name), ordinary embed")
  Phoenix.HTML.Form.input_name(f, :age)|> IO.inspect(label: "input_name(f, :age), ordinary embed")
end

for f <- PolymorphicEmbed.HTML.Form.polymorphic_embed_inputs_for(form, :polymorphic_children) do
  Phoenix.HTML.Form.input_name(f, :name) |> IO.inspect(label: "input_name(f, :name), polymorphic embed")
  Phoenix.HTML.Form.input_name(f, :age)|> IO.inspect(label: "input_name(f, :age), polymorphic embed")
end

Output:

input_name(f, :name), ordinary embed: "parent_schema[children][0][name]"
input_name(f, :age), ordinary embed: "parent_schema[children][0][age]"
input_name(f, :name), ordinary embed: "parent_schema[children][1][name]"
input_name(f, :age), ordinary embed: "parent_schema[children][1][age]"
input_name(f, :name), polymorphic embed: "parent_schema[polymorphic_children][name]"
input_name(f, :age), polymorphic embed: "parent_schema[polymorphic_children][age]"
input_name(f, :name), polymorphic embed: "parent_schema[polymorphic_children][name]"
input_name(f, :age), polymorphic embed: "parent_schema[polymorphic_children][age]"

This prevents forms with lists of polymorphic embeds from being correctly rendered/submitted.

AFAICS, this is a bug. If this is not the case, I would like to know what I'm missing and what the suggested workaround might be.

Thanks!

mathieuprog commented 6 months ago

This should be solved in latest release.