I have the following code in my simple LiveView form.
defmodule TodoappWeb.UserRegistrationLive do
alias Todoapp.Accounts
use TodoappWeb, :live_view
alias Todoapp.User
@impl true
def render(assigns) do
~H"""
<.form :let={f} id="registration-form" for={@form} phx-submit="save" phx-change="validate">
<h2 class="mt-6 text-center text-3xl font-extrabold text-gray-900">
Register
</h2>
<.input type="text" field={f[:firstname]} label="Firstname" />
<.input type="text" field={f[:middlename]} label="Middlename" />
<.input type="text" field={f[:lastname]} label="Lastname" />
<.input type="text" field={f[:email]} label="Email" />
<br />
<button
type="submit"
class="ml-3 inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
>
Register
</button>
</.form>
<br />
<.link patch={~p"/users/login"}>Login</.link>
"""
end
@impl true
def mount(_params, _session, socket) do
form =
%User{}
|> User.changeset(%{})
|> to_form(as: "form")
{:ok, assign(socket, form: form)}
end
@impl true
def handle_event("save", %{"form" => params}, socket) do
case Accounts.register_user(params) do
{:ok, _user} ->
{:noreply,
socket
|> put_flash(:info, "User registered successfully!")
|> redirect(to: ~p"/users/login")}
{:error, %Ecto.Changeset{} = changeset} ->
{:noreply,
socket
|> put_flash(:error, "Registration failed. Please check the errors below.")
|> assign(form: to_form(changeset))}
end
end
@impl true
def handle_event("validate", %{"form" => params}, socket) do
form =
%User{}
|> User.changeset(params)
|> Map.put(:action, :validate)
|> to_form(as: "form")
{:noreply, assign(socket, form: form)}
# {:noreply, assign(socket, params: params)}
end
end
Form renders fine and I see error messages when the fields are empty, but if I enter a value in a field and delete it the form crashes with the following error:
[error] GenServer #PID<0.5544.0> terminating
** (FunctionClauseError) no function clause matching in TodoappWeb.UserRegistrationLive.handle_event/3
(todoapp 0.1.0) lib/todoapp_web/live/user_registration_live.ex:44: TodoappWeb.UserRegistrationLive.handle_event("validate", %{"_target" => ["user", "lastname"], "user" => %{"email" => "", "firstname" => "d", "lastname" => "", "middlename" => ""}}, #Phoenix.LiveView.Socket<id: "phx-GAnDLL8TB_56nzlF", endpoint: TodoappWeb.Endpoint, view: TodoappWeb.UserRegistrationLive, parent_pid: nil, root_pid: #PID<0.5544.0>, router: TodoappWeb.Router, assigns: %{form: %Phoenix.HTML.Form{source: #Ecto.Changeset<action: :insert, changes: %{firstname: "d", lastname: "d"}, errors: [email: {"can't be blank", [validation: :required]}], data: #Todoapp.User<>, valid?: false, ...>, impl: Phoenix.HTML.FormData.Ecto.Changeset, id: "user", name: "user", data: %Todoapp.User{__meta__: #Ecto.Schema.Metadata<:built, "users">, id: nil, firstname: nil, middlename: nil, lastname: nil, email: nil, inserted_at: nil, updated_at: nil}, action: :insert, hidden: [], params: %{"email" => "", "firstname" => "d", "lastname" => "d", "middlename" => ""}, errors: [email: {"can't be blank", [validation: :required]}], options: [method: "post"], index: nil}, __changed__: %{}, flash: %{"error" => "Registration failed. Please check the errors below."}, live_action: :new}, transport_pid: #PID<0.5537.0>, ...>)
Not sure what I'm doing wrong?
You are using to_form
inconsistently.
In one place you call it with extra arg as: "form"
but in other places you omit this arg.
This :as
option changes the shape of your form.
Just add as: "form"
to everywhere you are calling to_form
and it will be fixed.