Phx-click being executed when rendering, not when clicking


I’m currently having this weird behavior on liveview 0.17.6

I have a button on a live component which has a phx-click

<button type="button" phx-click={publish()} class="action-button mt-2 sm:mt-0">
  <%= gettext("Publish") %> 1

the publish() function does the following:

  defp publish(js \\ %JS{}) do
    IO.inspect(js, label: "JS")
    |> JS.set_attribute({"value", "publish"}, to: "#new_planner_action")
    |> JS.dispatch("click", to: "#publish_button")

My big question is, why is that I’m getting this, on my terminal when the liveview reloads?

JS: %Phoenix.LiveView.JS{ops: []}

publish() is being called on render? why?

And when I click the button, nothing happens.

Any ideas?

1 Like

publish() is being called on render? why?

because you’re calling the function in your template. phx-click={publish()} is placing the output of the execution of the publish function as the value of the phx-click attribute. Most likely not what you want.

And when I click the button, nothing happens.

you should pass the name of the event to phx-click, and then handle the event in a handle_event callback in your view. Like here: Bindings — Phoenix LiveView v0.17.6

I’m not calling the function on my template… im telling to call the function when there is a phx-click. That’s straight from liveview documentation, on Bindings — Phoenix LiveView v0.17.6

alias Phoenix.LiveView.JS

def hide_modal(js \\ %JS{}, selector) do
  |> JS.push("modal-closed")
  |> JS.remove_class("show", to: selector, transition: "fade-out")
<button phx-click={hide_modal("#modal")}>hide modal</button>

Also added an example in this repo:

You’re right, my apologies. I’m not familiar with JS commands in LiveView and I now realize that they’re a different story.

Just a random thought: have you tried making publish() public?

Also, the only thins that phx-click does if you put anyhting inside is to convert that command into something like this:

<button type="button" phx-click="[[&quot;set_attr&quot;,{&quot;attr&quot;:[&quot;value&quot;,&quot;publish&quot;],&quot;to&quot;:&quot;#new_planner_action&quot;}],[&quot;dispatch&quot;,{&quot;event&quot;:&quot;click&quot;,&quot;to&quot;:&quot;#publish_button&quot;}]]" class="action-button mt-2 sm:mt-0">
Publish 1

is just an array defining what are the events that need to happen, what happens in each one of them, it should not be triggering anything.

No worries :), also there was a new version of livevew a couple of days ago.

Yes, tried public, same effect

1 Like

From my wannabe side of things looks like a bug, Ive open an issue to the liveview repo:

1 Like

The function is indeed called on render, this is why you see the result of IO.inspect. Nothing wrong with that, it is the expected usage.

The docs does not tell that the function is not called. It’s just that the phx-click accepts a JS struct as value and this is what the functions (from the doc and yours) return.

agreed, its called because is needs to transform what we put into phx-click, into something it understands (array of stuff)

It is called because you have {publish()} which is a function call, as @trisolaran said. So on render the function is called, calls IO.inspect, and then returns a JS struct that is serialized in the phx-click attribute.

To maybe make this a bit more clear: publish/0 constructs a command and is called at the time the template needs to know the command. The function is not called or executed when the returned command is executed by the javascript side.