JS.dispatch click not working

Hi,

Does anybody knows why this does not work at all?
When I click LAZY it tries to submit the form (it cant because there is no handle_event to handle it which is correct). But why it cant by clicking Publish have the same effect since JS is supose to perform a click on LAZY button?

defmodule JsformWeb.HomeLive do
  use JsformWeb, :live_view
  alias Phoenix.LiveView.JS

  def mount(_p, _s, socket) do
    changeset = %Ecto.Changeset{}
    {:ok, assign(socket, changeset: changeset)}
  end

  def render(assigns) do
    ~H"""
      <div>
        <.form let={f} for={@changeset} as="something" phx-submit="add-add">

          <%= submit gettext("LAZY"), id: "submit_button" %>
          <button type="button" phx-click={JS.dispatch("click", to: "#submit_button")}>
            Publish
          </button>
        </.form>
      </div>
    """
  end
end

Is this is some security thingy? from JS

Thanks

Sending a click CustomEvent (which is what JS.dispatch is doing in the background) doesn’t trigger the lazy button’s own click handler to submit the button, you have to do that yourself.

You can either add an event listener on the submit button, kind of like the example code in the docs

let lazy = document.getElementById("submit_button")
lazy.addEventListener("click", e => e.target.click())

Or catch the click on the lazy button and dispatch the submit to the form in idiomatic LiveView.JS

def render(assigns) do
  ~H"""
    <div>
      <.form for={@changeset} as="something" phx-submit="add-add" id="some_form">

        <%= submit "LAZY", id: "submit_button", 
            phx_click: JS.dispatch("submit", to: "#some_form") %>
        <button type="button"
            phx-click={JS.dispatch("click", to: "#submit_button")}>
          Publish
        </button>
      </.form>
    </div>
  """
end

Or just dispatch the form submit directly on the Publish button.

ignore the tired person

It does

 def render(assigns) do
    ~H"""
      <div>
        <.form for={:changeset} as="something" phx-submit="add-add" id="some_form">

          <%= submit "LAZY", id: "submit_button" %>
          <button type="button"
              phx-click={JS.dispatch("submit", to: "#some_form")}>
            Publish
          </button>
        </.form>
      </div>
    """
  end

  def handle_event("add-add", _, socket) do
    IO.puts("add-add")
    {:noreply, socket}
  end

I see the puts in log

I think it has to do with the form submission specs expecting a specific Event

See Form - Implicit submission and Event firing

Firing a click event at target means firing a synthetic pointer event named click at target.

  1. Let event be the result of creating an event using PointerEvent.

So the lazy button is expecting a PointerEvent, which is fired when you e.target.click() it, not a CustomEvent.

ignore the tired person

I think it works because your button type is “submit” here so this button is submitting, not the lazy one.

God yes, sorry. I was checking to see if that still submitted itself when clicked or if you had to have an actual type of submit or something. Then hurt myself in confusion.

I think you are correct on the custom events.

Haha, no worries. We have to be so deliberate on the details and can only hold so much in memory. Glad I could help to pair-code

Yes, to be a normal button , it needs to be specified has type=“button” or else is automatic type=“submit”

Wow I didn’t know that, thanks for the tip!

I always try to be explicit on the element types, helps with accessibility.

The reason why I’ need to do this is because I need to set an “state” just before I press the submit.
My case is that I have a kind of complex form with a SAVE and PUBLISH button. the problem is that you cannot have 2 submit buttons in a form and see which did one you clicked so you can have some logic when to SAVE or PUBLISH.

In my case what I wanted to do so solve this issue… was to have:

  • 1 hidden text field, that stores the action performed
  • 1 real submit button that is hidden
  • 1 fake submit PUBLISH button
  • 1 fake submit SAVE button

when you click fake SAVE, it triggers the phx-cli and calls JS.set_attribute on the hidden text field with “save” and do a JS.dispatch(“click”) on the hidden submit form

when you click fake PUBLISH, it triggers the phx-cli and calls JS.set_attribute on the hidden text field with “publish” and do a JS.dispatch(“click”) on the hidden submit form

My problem is that it does not “click” the “submit”

That should work without a hidden submit button by chaining the set_attribute with dispatch("submit", to: "#your-form")

Can you not track the form state via phx-change, then have <button type="button" phx-click="save_draft"/> and have that just save the current state?

Be careful, as JS.set_attribute wont over-write values, so if someone hit “save draft” then “publish”, you may still see the action as “save draft”. You have to chain JS.remove_attribute into set_attribute.

1 Like

hum… that would be an good alternative, will try that.

thanks

That totaly worked. Thanks.

def set_action(action \\ "save") do
    JS.remove_attribute("value", to: "#action") |> JS.set_attribute({"value", action}, to: "#action") |> JS.dispatch("submit", to: "#form-create-planner")
  end
<%= hidden_input f, :action, value: "save", id: "action" %>

 <%= submit "hi", id: "submit_button",
            class: "hidden"
          %>
<div
              phx-click={set_action("save")}
              class="action-button mt-2 sm:mt-0 cursor-pointer">
              <%= gettext("Save") %>
            </div>

<div
              phx-click={set_action("publish")}
              class="action-button mt-2 sm:mt-0 cursor-pointer">
              <%= gettext("Publish") %>
            </div>

I will get on the changeset

 "action" => "publish",

or

 "action" => "save",

There is an issue in chrome where node.dispatchEvent(new Event("click")) does not do the same thing as node.click(). Currently JS.dispatch does not special case anything, as it literally only calls dispatchEvent on The DOM apis. I may considering special casing click, but you can also add an event listener on the click and do this yourself, for example:

# phx-click={click("#foo")}
def click(to) do
  JS.dispatch("my:click", %{to: to})
end
window.addEventListener("my:click", ({detail}) => document.querySelector(detail.to).click())
2 Likes