Setting `phx-submit` value in JS hook for multiple Submit buttons

I have a form with 2 submit buttons. Save Draft and Save and Publish. I have found a solution which seems to be working but it feels lake a hack. Should I go for it or there is a better way to achieve this functionality?

Here is the solution (JS Hook and Submit buttons)

const PostFormHook = {
  mounted() {
    this.el.addEventListener("submit", (e) => this.submit(e));
  },
  submit(event) {
    const action = event.submitter.dataset?.action;
    if(!action) return;
    this.el.setAttribute("phx-submit", action)
  }
}

The submit buttons:

<button type="submit" data-action="save-draft" form="post-dialog">
  Draft
</button>
<button type="submit" data-action="save-and-publish" form="post-dialog">
  Publish
</button>

Handling form submits via js sadly stops the submit button value from being submitted, so this is afaik a shortcoming in the js api to work around.

You can have different submits with different value attributes in a form. I haven’t tried this but theoretically you could pattern match in the handle_event on that value to change the behavior of the submission.

Something like this might work:

<input type=“submit” value=“Save Draft” />
<input type=“submit” value=“Save and Publish” />

def handle_event(“save”, %{“post” => %{“submit” => “Save Draft”} = params}, socket)

That doesn‘t work with submits intercepted by any javascript, like when using liveview and phx-submit.

Actually you can modify form values in a Hook’s submit event unless you do event.preventDefault();

form.submit() doesn’t work with LV. As you have mention.

I’ve had the same issue and I believe I came up with something equally hacky. Been working in production for a year though. It works by submitting an extra field alongside the form which is then parsed by the liveview controller.

app.js

// Liveview on form submit doesn't push the value for on the submit button
// This hook adds the value of the click to a field called "submittedButton"
Hooks.SetFormSubmitValue = {
    mounted() {
        this.el.addEventListener('click', function(evt) {
            const trackingInput = document.querySelector('input[name="chart[submittedButton]"]');
            trackingInput.value = evt.target.getAttribute('name');
        });
    }
}

liveview template extra form fields

    <div>
       <input id="save" name="save" phx-hook="SetFormSubmitValue" value="Update Existing" type="submit">
       <input id="saveas"  name="saveas" phx-hook="SetFormSubmitValue" value="Create New" type="submit">
      <%= hidden_input(:chart, :submittedButton, value: "test") %>
    </div>

The controller will either receive the param submittedButton with the value “save” or “saveas”. The hidden input is there in the form, but it gets overridden.

Looks like mcrumm added support for SubmitEvent.submitter in a recently merged commit - Support form submitter (#2490) · phoenixframework/phoenix_live_view@847c704 · GitHub

As I understand it, this will allow us to add name and value attributes to submit buttons, which would propagate through to params… Allowing multiple submit buttons!

Confirmed, It works for me now (after updating to version 0.18.17+)
Able to get button name and value in params.
Thanks for the update on this post.

Thanks a bunch for this @rio517.

Just like you said, doing <input type="submit" name="action" value="some_action"> will indeed get %{"action" => "some_action") passed in as part of your params to the event handler.