mathieuprog / polymorphic_embed

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

During form submission, embedded arrays only contain the last entry #55

Closed gmorell closed 6 months ago

gmorell commented 2 years ago

I currently have a schema with this shape:

  schema "ClientInstance" do
    field :ext_ref, Ecto.UUID
    field :notes, :string
    embeds_one :query, EmbeddedSchema.ClientInstance
    timestamps()
  end

with the embedded schema as follows:

    embedded_schema do
    field :includes, {:array, :string}

    field :queries, {:array, PolymorphicEmbed},
          types: [
            queryquery: [module: Query, identify_by_fields: [:bar]],
            ...
          ],
          on_type_not_found: :ignore,
          on_replace: :delete
  end

Given a changeset like so:

#Ecto.Changeset<
  action: nil,
  changes: %{
    ext_ref: "6201d502-6422-4b49-9849-e07fd4fe5899",
    notes: "Misbehaving Tester",
    query: #Ecto.Changeset<
      action: :insert,
      changes: %{
        queries: [
          %Query{
            foo: nil,
            bar: "5642"
          },
          %Query{
            foo: nil,
            bar: "5672"
          }
        ]
      },
      errors: [],
      data: #EmbeddedSchema.ClientInstance<>,
      valid?: true
    > 
  },
  errors: [],
  data: #Controller.ClientInstance<>,
  valid?: true 
>

I have this rendering via polymorphic_embed_inputs_for/4 the form successfully against the existing data <%= polymorphic_embed_inputs_for query_form, :queries, :queryquery, fn fp -> %>, with the array of existing foo/bar fields present. On validation however, I only recieve a single one in the params passed back to the form as so:

%{
  "notes" => "Misbehaving Tester.",
  "query" => %{
    "includes" => [""],
    "queries" => %{
      "__type__" => "queryquery",
      "foo" => " ",
      "bar" => "5672"
    }
  }
}

as opposed to an array like I was expecting

%{
  "notes" => "Misbehaving Tester.",
  "query" => %{
    "includes" => [""],
    "queries" => [
    %{
      "__type__" => "queryquery",
      "foo" => " ",
      "bar" => "5642"
    },
    %{
      "__type__" => "queryquery",
      "foo" => " ",
      "bar" => "5672"
    }]
  }
}

I've been toying around with various solutions but I've reached a bit of an impasse at this time.

simonmcconnell commented 2 years ago

In my experience, you need to set the name of an input manually when working with array inputs. You can use the index and something like name="#{@name}[#{index}]".

mathieuprog commented 2 years ago

I think what would be interesting is to see the rendering phoenix template and generated html). It's probably the names of the inputs that are wrong.

woylie commented 1 year ago

The to_form implementation of phoenix_ecto checks the cardinality of a field and sets the name and id to id: name <> "[" <> index_string <> "]" and id <> "_" <> index_string respectively.

https://github.com/phoenixframework/phoenix_ecto/blob/4aa366cfc26b06433521a92deff27b52cec6d450/lib/phoenix_ecto/html.ex#L69

I think the to_form implementation of polymorphic_embed needs to check whether the field is of type {:array, PolymorphicEmbed} and modify the name and id attributes accordingly.

https://github.com/mathieuprog/polymorphic_embed/blob/05f682cd2328907a207085240e45c7e3f42afacf/lib/polymorphic_embed/html/form.ex#L117

mathieuprog commented 6 months ago

There has been a fix for form input rendering of list of embeds (now we render the index). It probably fixes this issue.