Inputs_for nested form error: construction of binary failed: segment 1 of type 'binary': expected a binary but got: nil

I’m attempting to create a nested form with duplicate inputs for two separate ticket holders. I’m using the <.simple_form> in LiveView, and everything I’ve seen is using different syntax (i.e. <% as opposed to <.)

I have this form working:

<.simple_form :let={f} for={%{tickets: []}}>
           <.input
            field={f[:text]}
            type="text" label="Enter text:"
            autocomplete="text"
            placeholder="Enter text" />
          <.input
            field={f[:option_id]}
            type="select"
            label="Choose your option:"
            options={Enum.map(options, fn option -> {option.name, option.id} end)}
          />
          <.input
            field={f[:date]}
            type="date"
            label="Choose a date:"
          />
          <.input
            field={f[:time]}
            type="time"
            label="Choose a time:"
          />
          <div class="flex gap-2">
            <div class="w-2/3">
            <.input
              field={f[:weight]}
              type="number"
              label="Weight"
              min="0"
            />
            </div>
            <div class="w-1/3 mt-1">
            <.input
              field={f[:weight_unit]}
              type="select"
              label="Unit"
              options={Enum.map(weight_units, fn weight -> weight.unit end)}
            />
            </div>
          </div>
          <:actions>
            <.button button_type="booking">Continue</.button>
          </:actions>
</.simple_form>

however when I try to add this at the top of the form to test (from the Phoenix docs):

<.inputs_for :let={fp} field={f[:tickets]}>
  <.input field={fp[:name]} type="text" />
</.inputs_for>

I receive this error:

construction of binary failed: segment 1 of type 'binary': expected a binary but got: nil

with this stack trace:

id = to_string(id || form.id <> "_#{field}")

Any idea on how to create a duplicate nested form of the current simple form with a nested inputs_for? Thanks!

1 Like

Not sure what could be the issue. I tried to reproduced it on my end and everything is working fine. See the code below:

Schemas

defmodule MyApp.Shop.ProductCategory do
  use Ecto.Schema
  import Ecto.Changeset

  schema "product_categories" do
    field(:name, :string)

    timestamps()
  end

  @doc false
  def changeset(product_category, attrs) do
    product_category
    |> cast(attrs, [:name])
    |> validate_required([:name])
    |> unique_constraint(:name)
  end
end

defmodule MyApp.Shop.Product do
  use Ecto.Schema
  import Ecto.Changeset

  schema "products" do
    field(:name, :string)
    field(:price, :decimal)
    belongs_to(:product_category, MyApp.Shop.ProductCategory)
    timestamps()
  end

  @doc false
  def changeset(product, attrs) do
    product
    |> cast(attrs, [:name, :price])
    |> validate_required([:name, :price])
    |> cast_assoc(:product_category)
  end
end

And then in my Form:

.simple_form
        for={@form}
        id="product-form"
        phx-target={@myself}
        phx-change="validate"
        phx-submit="save"
      >
        <.input field={@form[:name]} type="text" label="Name" />
        <.input field={@form[:price]} type="number" label="Price" step="any" />

        <.inputs_for :let={category_form} field={@form[:product_category]}>
          <.input type="text" field={category_form[:name]} label="category" />
        </.inputs_for>

        <:actions>
          <.button phx-disable-with="Saving...">Save Product</.button>
        </:actions>
      </.simple_form>

Am on LiveView 0.18.18 and Phoenix 1.7.2. Perhaps can you try to update the Phoenix and LiveView?

When you pass a map to to_form/1, it assumes said map contains the form parameters, which are expected to have string keys.

Maybe try string key for the map?

I do have those same LiveView and Phoenix versions. When I implemented your form, I got the following error:

key :form not found in: %{__changed__: %{flash: true, id: true}

with this stacktrace

<.simple_form
        for={@form}
        id="product-form"
        phx-target={@myself}
        phx-change="validate"
        phx-submit="save"
      >

You can use to_form(map, id: …), but the support for map based nested forms is not great. E.g. there’s no way to pass errors to nested inputs.

Im experiencing the same issue right now. Have you discovered a solution?

JK passing the as option to to_form fixed everything for me. For example:

to_form(%{
    "name" => "birthday.png",
    "meta" => %{byte_size: 1000, location: "path/to/file"} 
}, as: "image")
1 Like

this worked for me as well, thanks

I encountered the same issue and only managed to make it work when
I defined the form_data

form_data = %{
    "nested_field" => %{
        "1" => %{
          "subfield1" => "value 1.1",
        },
        "2" => %{
          "subfield1" => "value 2.1",
        }
    }
} |> to_form(id: "nested_form")

and used inputs_for with as containing field name and default containing empty list.

<.form :let={f} for={@form_data}>
  <.inputs_for :let={nf} field={f[:nested_field]} as={:nested_field} default={[]}>
    <.input field={nf[:subfield1]} type="text"/>
  </.inputs_for>
</.form>

:warning: The following scenarios result in errors

  • Not setting as on inputs_for correctly → construction of binary failed: segment 1 of type 'binary': expected a binary but got: nil
  • Not setting default on inputs_for → inputs are not rendered correctly, only one shows up and it is empty.
  • Using List instead of Map for nested_fielderrors were found at the given arguments: * 2nd argument: not a tuple