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