Is there an explanation of Map.put(:action, :insert) somewhere?

In a validate method on phx-change, it’s common to Map.put(:action, :insert) to the changeset. This is obviously used somehow by the form function. I’m having a bit of trouble deciphering it, as I can’t figure out where the action is received from the for map/struct. Probably missed it, my eyes are still adjusting to elixir.

What specifically does this do and are there more values that are used by the .form function?

Probably a mix of
https://hexdocs.pm/ecto/Ecto.Changeset.html#module-changeset-actions
and
https://hexdocs.pm/phoenix_html/Phoenix.HTML.Form.html#module-a-note-on-errors

4 Likes

Per the links @cpgo supplied, generally speaking, the Phoenix HTML functions will not display errors for a changeset unless it has an action value, which usually is something Ecto will apply as part of a bad insert or update. If you are doing other more manual things with the changeset, you need to assign the action yourself to have an outcome where the form presents the errors as expected.

I think it is a mistake that the code you are looking at is editing the map directly. You should probably lean on the apply_action/2 function to avoid making assumptions about the underlying Changeset map structure.

https://hexdocs.pm/ecto/Ecto.Changeset.html#apply_action/2

Hope this helps. I know it took me a bit to grok when I first started.

6 Likes

While I generally support your suggestion there‘s also nothing wrong with depending on the changeset structure, which is publicly documented and not going to change (in a breaking way) without a mayor version bump of ecto.

1 Like

Just wanted to note here that the official docs say it’s ok to update directly:

Ecto automatically applies the action for you when you call Repo.insert/update/delete, but if you want to show errors manually you can also set the action yourself, either directly on the Ecto.Changeset struct field or by using Ecto.Changeset.apply_action/2 .

(Emphasis mine.)

Probably explains why the generators use this pattern in live views:

  defp validate_thing(socket, thing_params) do
    changeset =
      socket.assigns.thing
      |> Things.change(thing_params)
      |> Map.put(:action, :validate)

    assign(socket, :changeset, changeset)
  end
3 Likes