To_form not setting errors from the changeset errors

Here’s where I assign_form:

defp assign_form(socket, %Ecto.Changeset{} = changeset) do
    form = to_form(changeset, as: "onboarding")
    IO.inspect(form)

    if changeset.valid? do
      assign(socket, form: form, check_errors: false)
    else
      assign(socket, form: form)
    end
  end

And here’s the inspect:

%Phoenix.HTML.Form{
  source: #Ecto.Changeset<
    action: nil,
    changes: %{},
    errors: [
      first_name: {"can't be blank", [validation: :required]},
      last_name: {"can't be blank", [validation: :required]}
    ],
    data: #TestWeb.OnboardingLive.StepsSchemas.Name<>,
    valid?: false
  >,
  impl: Phoenix.HTML.FormData.Ecto.Changeset,
  id: "onboarding",
  name: "onboarding",
  data: %TestWeb.OnboardingLive.StepsSchemas.Name{
    id: nil,
    first_name: nil,
    last_name: nil
  },
  hidden: [],
  params: %{"first_name" => ""},
  errors: [], <------------- I need errors to be here, from the changeset!
  options: [method: "post"],
  index: nil,
  action: nil
}

I’m using a standalone embedded schema, with no database backing to represent a step in a wizard I’m building:

defmodule TestWeb.OnboardingLive.StepsSchemas.Name do
  use Ecto.Schema
  import Ecto.Changeset

  embedded_schema do
    field :first_name, :string
    field :last_name, :string
  end

  def changeset(name, attrs \\ %{}) do
    name
    |> cast(attrs, [:first_name, :last_name])
    |> validate_required([:first_name, :last_name])
    |> validate_length(:first_name, min: 2)
    |> validate_length(:last_name, min: 2)
  end
end

The only thing I can think of is to_form somehow expects the name of the as: to be the same as the schema? I’m not sure. Appreciate the help!

Forms render errors only if the action is set on a changeset. Otherwise it’s expected to be a fresh/untouched changeset, which you might have created by running it through SomeSchema.changeset, but the user didn’t yet have a chance to fill in.

3 Likes

Thank you, I could not use apply_action since it was messing up a lot of things in the way to_form handles and expects inputs. So I just manually added the action and all is well.

def handle_event("validate", %{"name" => attrs}, socket) do
  changeset =
    %Name{}
    |> Name.changeset(attrs)

  changeset = %{changeset | action: :validate}

  socket =
    socket
    |> assign_form(changeset)

  {:noreply, socket}
end
1 Like

apply_action returns a schema struct, not a changeset, so that’s expected.

1 Like

Yep - so I had to just manually set the action so:

  1. The errors show up in the UI.
  2. I can still leverage the to_form.