I have a form that I want to be automatically submitted when a user presses “Enter” inside of its textarea field, but I want shift+enter to add a newline. So I setup a hook for this that includes this listener function:
function listener(event) {
if (event.key == "Enter" && !event.shiftKey && !event.repeat) {
event.preventDefault()
this.pushEventTo(this.el, "textarea-submit")
}
}
and I have some handle_event branches that look like this:
def handle_event("textarea-submit", params, socket) do
save_comment(socket, socket.assigns.action, socket.assigns.form.params)
end
def handle_event("save", %{"comment" => comment_params}, socket) do
save_comment(socket, socket.assigns.action, comment_params)
end
They work exactly how you’d expect except that if the comment is saved via the “save” event getting triggered, the textarea gets cleared like you’d expect. If it is saved via the “textarea-submit” event, the comment saves just fine, but the textarea retains the value that was there before submission.
Any ideas why this might be happening? I don’t think it’s anything in the handle_event code because the “textarea-submit” branch works fine if I change the form element’s code to phx-submit="textarea-submit". It seems to be something about the fact that it’s triggered from the JavaScript side.
The JavaScript client is always the source of truth for current input values. For any given input with focus, LiveView will never overwrite the input’s current value, even if it deviates from the server’s rendered updates.
Have you considered the form’s usability for mobile users, where there is no shift-enter?
Overriding enter as new line seems counterintuitive to the generally expected UX of textareas and would probably cause friction and frustration for most users.
That’s a good point; I think I’ll change it to control/command + enter. There’s also a button to do it for places where the shortcuts aren’t an option.
Also I was looking into the original question of programmatically submitting a form and found that there are two form submit functions, submit() and requestSubmit(). I wasn’t aware of the second one, which forces the browser to run client-side validations, vs. plain submit which just submits without validation. Not sure how this plays with LiveView, but something to be aware of?
So in your hook you can probably capture and prevent default on ctrl/cmd-enter and just call this.el.form.requestSubmit() without needing to create and dispatch a custom event, or push_event back to the server.
Ooh, thank you for this! requestSubmit does seem to play very nicely and it feels much less awkward than the click-event simulation I was doing before.
One caveat for requestSubmit() is that it’s only supported on Safari >= 16, which, as of this post, is less than a year old. Still, browser coverage is pretty good at 89.75%