Correct way to store deleted state of associations in Phoenix Form?

I’ve followed several past posts (eg 51569) about how to delete associations from forms, but am hitting another issue. I can tell the Changeset to remove the associations (a belongs_to and has_many) and the Changeset and Form behave correctly. But if any other change in my form happens, these associations are resurrected. It appears having no data for those fields overwrites the Changeset’s stored :replace data. My current workaround is to put_new empty values for these fields to guarantee they exist:

  @impl true
  def handle_event("validate", %{"element" => element_params}, socket) do
    element_params =
      element_params
      |> Map.put_new("schedule", nil)
      |> Map.put_new("element_photos", [])

    changeset =
      socket.assigns.element
      |> Elements.change_element(element_params)
      |> Map.put(:action, :validate)

    {:noreply, assign_form(socket, changeset)}
  end

I realize this is a hack. What should I be doing in my Form to let it be the source of truth?

Thanks!

This one shows a way around that.

Ah nice, you’re setting the :delete key back to the changeset so it persists in the form. That’s a missing piece from when I tried using a virtual field like that solution.

I’m using the new Ecto :drop_param, which marks the record as :replace. Those records are in my to_form’s changes along with their indexes in the drop_param list, but they don’t end up rendered by inputs_for. I wonder if I’m missing something with drop_param, or if I found an edge case like the new sort_param.

If that’s the case then this is indeed problematic with LV. The form needs to retain deleted items, when they had been persisted before, as “to be deleted” till it is eventually saved.

Got it, thanks! I’ve opened a LiveView ticket for this.

Jose showed me that if you include an empty drop_param input in your form, it will keep the changes as expected. In my case :

<input type="hidden" name="element[element_photos_drop][]" />             <!-- The fix -->
<.inputs_for :let={element_photo_form} field={@form[:element_photos]}>

Note this requires the (currently) latest version of Ecto (3.10.1).