Phoenix_live_view: Identify which submit button was used when phx-submit event is called

Created on 2 Dec 2019  Â·  6Comments  Â·  Source: phoenixframework/phoenix_live_view

Environment

  • Elixir version (elixir -v): Elixir 1.8.1 (compiled with Erlang/OTP 20)
  • Phoenix version (mix deps): 1.4.11
  • Phoenix LiveView version (mix deps): 0.4.1
  • NodeJS version (node -v): v8.11.3
  • NPM version (npm -v): 6.12.0
  • Operating system: Win 10 Pro 1803

Actual behavior

I have a form that I use to create/edit logs. Here's a cut down version of the schema:

  schema "captains_log" do
    field :comment, :string
    field :work, :boolean, default: false
    belongs_to :user, Zoinks.Accounts.User

    timestamps()
  end

I already have a version of the app that uses a traditional POST:

<%= form_for @changeset, @action, fn f -> %>
  <%= textarea(f, :comment) %>

  <%= submit "Work", name: "work" %>
  <%= submit "Break", name: "break" %>
<% end %>

In my LogController, I have an :update method that uses the following technique to determine which button was pressed:

def update(conn, %{"log" => log_params } = params) do
  is_work = Map.has_key?( params, "work" )

  log_params = Map.put( log_params, "work", is_work )

  # Do stuff with log_params
end

Now I'm trying to support the two button UI with Live View. So I've set the event handler for the form:

<%= form_for @changeset, @action, [phx_submit: :save], fn f -> %>

And here's what's currently in my event handler:

def handle_event("save", %{"log" => log_params} = params, socket) do
  IO.inspect params

  { :noreply, socket }
end

When I look at the console, I can see that Live View does not include the name/id of the button that triggered the event. There is no convenient way to find out this information.

Expected behavior

Well, I wasn't expecting it to work. But I was _hoping_ 😬 that there might be a simple trick that I could implement. This issue was also raised in https://github.com/phoenixframework/phoenix_live_view/issues/184. I assume the best way forward with the current version is to write a little bit of javascript that will set a hidden field in the form (before the event is triggered)

It would be nice if Live View somehow captured this information and passed it up as a param for the phx-submit event. Then I wouldn't have to implement anything. Doing so would mean that I could fall back to my POST/LogController code when javascript isn't enabled 😸

wontfix

Most helpful comment

Just to follow up for a little more clarity for a path forward @mitkins...

  • Server side form submits can be detected with formaction and specifying a specific url per button. However, LV is not this case.

  • Client side submits leave you with only 2 good options (note: button[value] only seems to work in Safari)

  1. Don't have multiple submit buttons.

or

  1. Since this is such a common example when creating forms, one can do the following:
<form>
  <input type="hidden" name="submittedButton" />

  <button type="submit" name="foobar" phx-hook="SetButtonState">Save</button>
  <button type="submit" name="oofda" phx-hook="SetButtonState">Save and Close</button>
</form>

// with a hook `mount` for this button
this.el.addEventListener('click', function(evt) {
  const trackingInput = document.querySelector('input[name="submittedButton"]');
  trackingInput.value = evt.target.getAttribute('name');
});

And then pattern match on the submitted values on the Elixir side. One caveat though - You will also have to handle user's submitting the form with their keyboard.

Perhaps this would be a small useful post if things are a tad confusing and maybe I can explore this more in depth. Does that sound useful?

All 6 comments

This probably won't work, but have you tried:

<%= submit "Work", name: "submit_type", value: "work" %>
<%= submit "Break", name: "submit_type", value: "break" %>

Just tried it. Unfortunately, no...

Unfortunately JavaScript does not keep track at all of which button was pressed, so unfortunately we cannot solve this at all, as noted in #184. :/

Just to follow up for a little more clarity for a path forward @mitkins...

  • Server side form submits can be detected with formaction and specifying a specific url per button. However, LV is not this case.

  • Client side submits leave you with only 2 good options (note: button[value] only seems to work in Safari)

  1. Don't have multiple submit buttons.

or

  1. Since this is such a common example when creating forms, one can do the following:
<form>
  <input type="hidden" name="submittedButton" />

  <button type="submit" name="foobar" phx-hook="SetButtonState">Save</button>
  <button type="submit" name="oofda" phx-hook="SetButtonState">Save and Close</button>
</form>

// with a hook `mount` for this button
this.el.addEventListener('click', function(evt) {
  const trackingInput = document.querySelector('input[name="submittedButton"]');
  trackingInput.value = evt.target.getAttribute('name');
});

And then pattern match on the submitted values on the Elixir side. One caveat though - You will also have to handle user's submitting the form with their keyboard.

Perhaps this would be a small useful post if things are a tad confusing and maybe I can explore this more in depth. Does that sound useful?

Thank you for providing the example. I have successfully applied it to my code. Ah, do you mean in the case where the user presses enter in a text input? I'm assuming that submittedButton.value would just be an empty string in this case?

Sadly this solution doesn't work reliably; About 25% of the time the form gets submitted before the hook changes the value and the process receives an empty string for submittedButton.

Was this page helpful?
0 / 5 - 0 ratings