I’m using LiveView to create an edit-page. Firstly the data is loaded into a changeset
def mount(%{"id" => id} = params, session, %{assigns: %{current_user: current_user}} = socket) do
changeset =
case id do
nil -> Subscription.changeset(%Subscription{})
id -> sub =
case Subscription.get_sub_by_id(id, current_user) do
{:ok, sub, _msg} -> sub
{:error, _, msg} -> %Subscription{}
end
|> Ecto.Changeset.change()
end
At this moment, all the data from the database is stored in changeset.data.
One of the fields in my model has an embeds_many relation, as in
%Subscription{..., medicines: [%Medicine{...}, %Medicine{...}, %Medicine{...}]
My LiveView has a button to remove or add a medicine. To resolve this, I first tried this:
def handle_event("removeMedicine", %{"index" = index}, %{assigns: %{changeset: changeset}} = socket) do
medicines =
changeset.changes.medicines
|> List.delete_from(String.to_integer(index))
changeset =
changeset
|> Ecto.Changeset.put_embed(:medicines, medicines)
{:noreply, assign(socket, changeset: changeset)}
end
This doesn’t work, because the changeset.changes is empty. Everything is stored in changeset.data.
I found a solution but its with a big detour. I first copy all medicines from changeset.data into changeset.changes and then delete the correct one from there:
def handle_event("removeMedicine", %{"index" => index}, %{assigns: %{changeset: changeset}} = socket) do
#Index of removed item
index = String.to_integer(index)
#Merge changeset.data, changeset.changes and remove item at index
medicines=
Map.get(changeset.data, :medicines, []) ++ Map.get(changeset.changes, :medicines, [])
|> List.delete_at(index)
#Remove changeset.data and add all values to changeset.changes
changeset =
if (Map.has_key?(changeset.data, :medicines)) do
put_in(changeset.data.medicines, [])
|> Ecto.Changeset.put_embed(:medicines, medicines)
else
changeset
|> Ecto.Changeset.put_embed(:medicines, medicines)
end
{:noreply, assign(socket, changeset: changeset)}
end
What would be ‘the better’ approach to do this?