polymorphic_embed icon indicating copy to clipboard operation
polymorphic_embed copied to clipboard

During form submission, embedded arrays only contain the last entry

Open gmorell opened this issue 3 years ago • 3 comments

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.

gmorell avatar Apr 27 '22 20:04 gmorell

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}]".

simonmcconnell avatar Jun 03 '22 09:06 simonmcconnell

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.

mathieuprog avatar Jul 14 '22 15:07 mathieuprog

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

woylie avatar Dec 04 '22 22:12 woylie