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.
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 😸
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)
or
<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.
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
formactionand 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)or
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?