Do you maintain both @data and @form that is based on @data as the "source of truth"?

Hey all, with the modifications to a form coming from outside and expected to land in the @form fields, for the end user to observe and modify if needed, how do you go about maintaining the rest of the fields at there not-yet-saved states, so that they are not overwritten by the external change?

If the validation message only modifies the @form, the data itself is not reflecting these changes anymore, so there’s no way to base any incoming modifications off of in the socket. Does this implies the the @form should be considered the “source of truth”? If so, is it by directly hacking of it in the socket is how we achieve a few fields patching?

Why would we keeping the data in the socket alongside @form if it does not reflect the form changes?

I do that like below.

@impl true
def handle_event("event", %{"data" => data}, socket) do
  your_params = %{...}

  data_params =
    |> Map.merge(your_params)

  form =
    DataParams.changeset(%DataParams{}, data_params)
    |> to_form(as: :data)
    |> Map.put(:action, :validate)

  socket = socket |> assigns(:form, form)

  {:noreply, socket}

To make it easily, I made a simple helper library.


Thank you @nallwhy! I saw your helper library earlier, nice work!

I wonder, since originally, when an existing item is just open for editing, and nothing is yet modified, socket.assigns.form.source.params appear to be empty, essentially resulting in clearing all of the fields, except that incoming data. How would you deal with a form open for editing in that case?

At the moment, the way I see the difference between the resource assign vs the form assign for a given resource is that the former reflects the persisted db state while the latter reflects transient form state. It can be useful to have both because it allows for things like easy reverting/resetting of form state or comparing/displaying currently persisted and transient working state in the LiveView.