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)
1 Like

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.

1 Like