How does Phx.Gen.Auth log-In form work without handle_event?

I want to create my own form for User Login. I have the code that was generated with the new Live Phx.Gen.Auth, but I don’t want to use <.simple_form>

The issue is that the generated form doesn’t throw any handle_events (which is a total mystery to me). My form keeps throwing handle_events which then create errors.

Below is my form. I’ve left out elements that are irrelevant to the problem (like the link if you don’t have an account).

I get this error:
handle_event/3 is undefined or private

I don’t understand how the generated form is logging in the user without any handle_events. This is super confusing and the crux of why I can’t translate the generated form into my own form. The action points back to the file that has no handle_events so I don’t understand where the login functionality is being called.

Could someone help me do the translation:

MY FORM:

Note: <.form_save_button> just formats the button. The action translates into phx-click. I’ve tried giving the button “submit” and “update.” Neither work. It still tells me that I’m missing handle_event.

<.form
            id="login-form"
            :let={f}
            for={@changeset}
            as={:user}
            phx-update="ignore"
            action={~p"/users/log_in"}>

                <%= error_tag f, :email %>
                <%= email_input f, :email, required: true, placeholder: "Email", class: "skl-form-input-short" %>

                <%= error_tag f, :password %>
                <%= password_input f, :password, required: true, value: input_value(f, :password), placeholder: "Password", required: true,
                    name: "password", id: "password_for_email",
                    class: "skl-form-input-short" %>

              <div class="flex flex-row items-center">
                <%= checkbox f, :remember_me, value: false, unchecked_value: false %>
                <div class="md:ml-1 font-sans text-sm text-white">Keep me logged in</div>
              </div>

              <.link href={~p"/users/reset_password"} class="text-sm font-semibold">Forgot your password?</.link>

              <.form_save_button action="submit" add_class="mb-4">Sign in <span aria-hidden="true">→</span></.form_save_button>
</.form>

AUTO GENERATED FORM:

<.simple_form for={@form} id="login_form" action={~p"/users/log_in"} phx-update="ignore">
            <.input field={@form[:email]} type="email" label="Email" required />
            <.input field={@form[:password]} type="password" label="Password" required />

            <:actions>
              <.input field={@form[:remember_me]} type="checkbox" label="Keep me logged in" />
              <.link href={~p"/users/reset_password"} class="text-sm font-semibold">
                Forgot your password?
              </.link>
            </:actions>
            <:actions>
              <.button phx-disable-with="Signing in..." class="w-full">
                Sign in <span aria-hidden="true">→</span>
              </.button>
            </:actions>
</.simple_form>

Getting closer, but still lost on one point.

Read this article on phx-trigger-action.

So I understand that the action in the form that points to ~p/users/log_in is directing control to the router and (in theory) to the SessionController :create function. BUT … that path is in TWO places in my router:

In my Router, this path goes to two places:

live_session :redirect_if_user_is_authenticated,
...
      live "/users/log_in", SketcherLoginLive, :new
    end

and it’s also:

post "/users/log_in", UserSessionController, :create

So if I use that path in my form, how does it know to go to the “post” version of that path instead of the Live one? I think that’s what is causing the problem.

All HTTP requests have a verb, commonly GET and POST. Live routes are GET. When you submit a form, the form is configured to do a POST, matching your controller route.

Great job on putting the pieces of the puzzles together!

I didn’t quite figure it out. What action is the auto-generated button calling? I can’t figure it out from the component code. Mine was calling “submit” and triggering a handle_event. That’s why I couldn’t get past that error. But when I put your <.button> code back in, it worked. But I want to use my own component, so I need to understand how your button was submitting the form but not triggering an event.

1 Like

The method is an attribute in the form tag. Check the .simple_form component and the generated HTML. In case you are unfamiliar, check here: Sending form data - Learn web development | MDN

To POST, you will need a form, even if it always has just a single button inside.

1 Like

Thank you!