Form not firing phx-change in LiveView

Hi I want to make the most boring on/off switch with form and checkbox.

The problem I have is that when the checkbox is clicked on, no event is fired and I can’t understand why. I’ve tried setting hidden_input to true explicitly, but it didn’t help.

<.form let={f} for={:system_switch} action="" phx-change="switch">
      <%= checkbox(f, "system_switch",
            checked_value: :on,
            unchecked_value: :off,
            value: :on)%>
</.form>

EDIT: Seems that the event is not firing at all for any kind of input - whether it was created with one of the Form functions or manually.
I have checked whether a phx-click works - and it does. (this was just to make sure my liveview is fine.

I have no idea where to even start looking. I would really appreciate any hint

2 Likes

That code looks like it should work at first glance, so something else may be going on.

A common cause of mysterious form behavior (especially for small forms like this one) is accidentally nesting them inside another form element.

Also check the browser console for errors from the LiveView JS.

3 Likes

Indeed it is, it happened as I was porting a much older system to liveview.
Two things are strange to me:

  1. that phx-click works, but phx-change not
  2. I’ve noticed in the console of the running system that there is an attempt to reach two inexistent routes: /phoenixlive_reloadsocketwebsocket and /socketwebsocket

The second one is particularly important as it points to a possible typo somewhere. However, looking at LiveView github, I can’t find where something like /phoenixlive_reloadsocket or /socket would be sent. Any pointers there?

Phoenix is using the standard webplatform events for forms for submit/change detection. Nesting forms is not allowed based on the html spec, so you cannot expect nested forms to properly emit events still. Click events are not affected by the nested forms however.

There are no nested forms. This is the only form element.

Ok, then I have misunderstood your prev. reply.

If it’s of any help, I copied your example code and created a handle_event for phx-change and it worked just fine.

def handle_event("switch", params, socket) do
    IO.inspect params
    {:noreply, socket}
end

IEx output

%{
  "_csrf_token" => "...",
  "_target" => ["system_switch", "system_switch"],
  "system_switch" => %{"system_switch" => "off"}
}

Thank you for confirming @muelthe!

Does anyone know, where on client side is the click handled? I would try to follow if browser recognizes it at all

And the solution is :drum::drum::drum:

The form was within a table, which is not allowed by html 5 and the form tag actually doesn’t render.


Because the form tag doesn’t render, no phx- actions are placed in the DOME…

Thanks everyone for helping out!

1 Like

Thank you so much for this, this saved me a lot of time!

I spent a few hours trying to figure out why a phx-change event was firing on a text input field, but not on the form that contained it. Sure enough, my liveview component containing the form was rendering inside a bigger form. Moving my component outside of the bigger form fixed the problem.

Thank you!

PS Since the observable symptoms of this problem could be so mysterious (*), I wonder if there is a reasonably simple way to detect and flag this condition (at runtime?), to save future devs debugging time.

(*) My initial symptom was that the value of my input field (<input type="email" value={@val} phx-change=...>) would stop reacting to changes in @val, as soon as I click a button on the form. To make things even more mysterious / random, clicking a div (<div phx-click=...>CLICKME</div>) before typing into the text input, the problem would go away.