Inputs_for is triggering the events, but not showing the child fields

Greetings Everyone!!!

Yesterday I had working the sample in Phoenix.Component — Phoenix LiveView v1.0.4, but this morning, I included a few <div> for organization purposes, and it stopped working.

My problem is that I had already re-written the whole example about 4 or 5 times, and it is still doing the same thing. It shows the handle_event("validate", ...) running, but the browser is not showing the child’s table field, as yesterday did.

I even started a tiny new app, re-write the sample one more time, and it has the same behavior, the log show the event running, but not showing the child’s table field.

Yesterday it was adding and removing records just fine. But today I have not been able to replicate the inputs_for working.

I just created a new Phoenix app, and used these commands to create the tables for the sample.

mix phx.gen.live General Customer customers cst_name
mix phx.gen.live General Consignee consignees cns_name cns_cust:references:customers
mix ecto.migrate
# app/general/consignee.ex

defmodule App.General.Consignee do
  use Ecto.Schema
  import Ecto.Changeset

  schema "consignees" do
    field :cns_name, :string

    ##### new code ######
    belongs_to :customers, App.General.Customer, foreign_key: :cns_cust
    #### end new code ###

    timestamps(type: :utc_datetime)
  end

  @doc false
  def changeset(consignee, attrs) do
    consignee
    |> cast(attrs, [:cns_name])
    |> validate_required([:cns_name])
  end
end
# app/general/customer.ex

defmodule App.General.Customer do
  use Ecto.Schema
  import Ecto.Changeset

  schema "customers" do
    field :cst_name, :string

    ### New Code #####
    has_many :consignees, App.General.Consignee,
      foreign_key: :cns_cust, on_replace: :delete
    ## End New Code ##

    timestamps(type: :utc_datetime)
  end

  @doc false
  def changeset(customer, attrs) do
    customer
    |> cast(attrs, [:cst_name])
    |> validate_required([:cst_name])
    ##### New Code #####
    |> cast_assoc(
      :consignees,
      with: &App.General.Consignee.changeset/2,
      sort_param: :consignees_sort,
      drop_param: :consignees_drop
    )
    ##### End New Code #####
  end
end
# app_web/live/customer_live/form_component.ex

defmodule AppWeb.CustomerLive.FormComponent do
  use AppWeb, :live_component

  alias App.General

  @impl true
  def render(assigns) do
    ~H"""
    <div>
      <.header>
        {@title}
        <:subtitle>Use this form to manage customer records in your database.</:subtitle>
      </.header>

      <.simple_form
        for={@form}
        id="customer-form"
        phx-target={@myself}
        phx-change="validate"
        phx-submit="save"
      >
        <.input field={@form[:cst_name]} type="text" label="Cst name" phx-debounce="blur" />

      <!-- ####### NEW CODE ######## -->
        <.inputs_for :let={cc} field={@form[:consignees]}>
          <input type="hidden" name="customers[consignees_sort][]" value={cc.index} />
          <.input type="text" field={cc[:cns_name]} placeholder="Customer Name" phx-debounce="blur" />
          <button
            type="button"
            name="customers[consignees_drop][]"
            value={cc.index}
            phx-click={JS.dispatch("change")}
          >
            Delete
          </button>
        </.inputs_for>
        <input type="hidden" name="customers[consignees_drop][]" />
        <button type="button" name="customers[consignees_sort][]" value="new" phx-click={JS.dispatch("change")} >
          Add
        </button>
      <!-- ####### END NEW CODE ######## -->

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

  @impl true
  def update(%{customer: customer} = assigns, socket) do
    {:ok,
     socket
     |> assign(assigns)
     |> assign_new(:form, fn ->
       to_form(General.change_customer(customer))
     end)}
  end

  @impl true
  def handle_event("validate", %{"customer" => customer_params}, socket) do
    changeset = General.change_customer(socket.assigns.customer, customer_params)
    {:noreply, assign(socket, form: to_form(changeset, action: :validate))}
  end

  def handle_event("save", %{"customer" => customer_params}, socket) do
    save_customer(socket, socket.assigns.action, customer_params)
  end

  defp save_customer(socket, :edit, customer_params) do
    case General.update_customer(socket.assigns.customer, customer_params) do
      {:ok, customer} ->
        notify_parent({:saved, customer})

        {:noreply,
         socket
         |> put_flash(:info, "Customer updated successfully")
         |> push_patch(to: socket.assigns.patch)}

      {:error, %Ecto.Changeset{} = changeset} ->
        {:noreply, assign(socket, form: to_form(changeset))}
    end
  end

  defp save_customer(socket, :new, customer_params) do
    case General.create_customer(customer_params) do
      {:ok, customer} ->
        notify_parent({:saved, customer})

        {:noreply,
         socket
         |> put_flash(:info, "Customer created successfully")
         |> push_patch(to: socket.assigns.patch)}

      {:error, %Ecto.Changeset{} = changeset} ->
        {:noreply, assign(socket, form: to_form(changeset))}
    end
  end

  defp notify_parent(msg), do: send(self(), {__MODULE__, msg})
end

Whenever I press the button to Add a New Consignee, the server shows:

[debug] HANDLE EVENT "validate" in App.Web.CustomerLive.Index
  Component: AppWeb.CustomerLive.FormComponent
  Parameters: %{"_target" => ["customers", "consignees_sort"], "customer" => %{"cst_name" => "Sample"}, "customers" => %{"consignees_drop" => [""], "consignees_sort" => ["new"]}}
[debug] Replied in 209µs

I can see the consignees_sort and the consignees_drop in the log. It is trying to do its job, but the browser does not show any change.

If someone finds what I did wrong, or if anybody creates a tiny app with the inputs_for working, I will gladly compare that app with mine to find out what I have missing, or what is different.

Thanks in advance and best regards,

Greg

Hey Greg, try swapping customer singular for customers plural when specifying the html name attribute in the markup e.g.

<button type="button" name="customer[consignees_sort][]" value="new" phx-click={JS.dispatch("change")} >
  Add
</button>

Here you go! live_cal/lib/live_cal_web/live/calendar_live/form_component.ex at 3af57942fe6460a16e4391f1cc5b1b64189ba009 · codeanpeace/live_cal · GitHub

I am feeling so stupid right now. That was it. Thank you very much!!!

Best regards,

Greg