Hello All,
I have a customer
schema with nested contacts
that’s defined as an embedded schema that I render in a liveview form component. I’m trying to add and delete the nested contact
dynamically. Adding works just fine but when I trigger a remove action, I can’t see the param for the newly added contact
in the handle_event
callback … not sure how to go about getting this to work properly, any advice on what I’m doing wrong here please?
defmodule MyApp.Customer do
use Ecto.Schema
import Ecto.Changeset
schema "customers" do
field :name, :string
embeds_many :contacts, MyApp.ContactDetail
timestamps()
end
@doc false
def changeset(customer, attrs) do
customer
|> cast(attrs, [:name])
|> cast_embed(:contacts)
|> validate_required([:name])
end
end
defmodule MyApp.ContactDetail do
use Ecto.Schema
import Ecto.Changeset
embedded_schema do
field :temp_id, :string, virtual: true
field :contact_type, Ecto.Enum, values: [:address, :phone, :email]
field :details, :string
end
def changeset(embedded_schema, attrs) do
embedded_schema
|> cast(attrs, [:contact_type, :details, :temp_id])
|> validate_required([:contact_type, :details])
end
end
In my customer_live/form_component.html.heex
, I have something like this for the contacts section:
<.form
let={f}
for={@changeset}
id="customer-form"
phx-target={@myself}
phx-change="validate"
phx-submit="save">
<%= for c <- inputs_for(f, :contacts) do %>
<%= hidden_inputs_for(c) %>
<%= text_input c, :details %>
<%= hidden_input(c, :temp_id) %>
<a href="#"
phx-target={@myself}
phx-click="remove-contact"
phx-value-contact={c.data.temp_id}>×</a>
<% end %>
</.form>
In the customer_live.form_component
, the handle_event("remove-contact", %{"contact" => temp_id}, socket)
gets called but the temp_id
is empty. I tried referencing it like c.temp_id
but that blows up with an error.
def handle_event("add-contact", _, socket) do
existing_contacts = Map.get(socket.assigns.changeset.changes, :contacts, [])
new_contacts = [%{temp_id: :rand.uniform(1000)} | existing_contacts]
changeset = Ecto.Changeset.put_embed(socket.assigns.changeset, :contacts, new_contacts)
{:noreply, assign(socket, changeset: changeset)}
end
def handle_event("remove-contact", %{"contact" => contact_id}, socket) do
new_contacts = socket.assigns.changeset.changes
|> Map.get(:contacts)
|> Enum.reject(&(&1.changes.temp_id == contact_id))
changeset = Ecto.Changeset.put_embed(socket.assigns.changeset, :contacts, new_contacts)
{:noreply, assign(socket, changeset: changeset)}
end
I’m using liveview 0.17.5 btw.
Appreciate any help, thanks!