Dynamic form submission with liveview

Hi,

There are quite a few issues that describes the current situation when you want to distinguish which button was pressed to submit a form with liveview.

Here is my new hack to tackle this problem using current liveview implementation (0.17.5).
I’ve added this function to my app.js to properly factorize the code (but this is not mandatory, it could be put into the template file as well)

// webapp.submit submits the form form_id using event bypassing liveview phx-submit default
// ----------------------------------------------------------------------------------------
webapp.submit = (form_id, event) =>
  webapp.live_socket.execJS(document.getElementById(form_id),
    `[["push", {"event": "${event}"}]]`, 'submit')

note: webapp is my toplevel object in which I also stored the liveSocket

Then in the template you can simply create your buttons like here (extract from a modal template)

   <section class="modal-card-body thin_scroll">
      <.form let={f} for={@changeset} id="form_modal" phx-submit="save_goal" as="goal">
          ... your form content ....
      </.form>
    </section>
    <footer class="modal-card-foot">
      <button class="button is-primary" onclick="webapp.submit('form_modal', 'save_decision')"><%= gettext("Decision") %></button>
      <button class="button is-primary" onclick="webapp.submit('form_modal', 'save_information')"><%= gettext("Information") %></button>
      <button class="button is-primary" form="form_modal" type="submit"><%= gettext("Validate") %></button>
    </footer>

Here I have kept a traditionnal submit button in third position that will trigger the default phx-submit event.

Finally, you only have to pattern match to get the wanted behaviour in you .ex file

def handle_event("save_decision", params, socket),
    do: handle_event("save_goal", Map.put(params, "type", "decision"), socket)

  def handle_event("save_information", params, socket),
    do: handle_event("save_goal", Map.put(params, "type", "Information"), socket)

  def handle_event("save_goal", params, %{assigns: assigns} = socket) do
    Logger.info("params: #{inspect(params)}")
    ...
    {:noreply, socket}
  end

And that’s it !
Cheers,

Sébastien

2 Likes

Keep in mind the JS format is not public, so building the cmd string yourself is not something you should do. What you can do instead is render your dynamic JS commands as html attributes, then your execJS code can do

let form = document.getElementById(form_id)
liveSocket.execJS(form, form.getAttribute(`data-submit-${event}`), "submit")

and in your template: <.form ... data-submit-foo={JS.push("foo")}

2 Likes

Hi Chris,

Yes that’s right and that’s why I mention it is a hack.

But right now, I find it more readable that way and I know that it may be broken in the future, but I guess it will not be to much work to follow the code evolutions (I’ve done much more work to date to move from 0.1.0 up to the last 0.17.5 :wink: )

Anyway, it’s good for the reader to know the RIGHT way of doing it !

I take this opportunity to thank you for your hard work on phoenix and specially liveview,
Cheers,
Sébastien.

Hi seb3s, I’m curious about this hack - what method do you use to test “clicking” these alternate form buttons?