Hi Folks,
I’m writing a simple LiveView form with plain Map (no Ecto changesets here).
The idea is quite simple: when we first load, pick a default channel, and, on every submission, keep the last channel but clear the message (yes, this is yet-another-chat example).
The first time it submits, it works properly - but then - the following submissions don’t.
The whole file:
defmodule SimpleLiveChat do
use AppWeb, :live_view
def mount(_params, _session, socket) do
empty = to_form(%{"channel" => "lobby"}, as: "chat")
{:ok, assign(socket, form: empty)}
end
def render(assigns) do
~H"""
<Layouts.app flash={@flash}>
<.form id="message-form" for={@form} phx-submit="send">
<.input field={@form[:message]} label="Message" />
<.input field={@form[:channel]} label="Channel" />
<.button variant="primary"> Send </.button>
</.form>
</Layouts.app>
"""
end
def handle_event("send", %{"chat" => params}, socket) do
channel = params["channel"]
f = to_form(%{"channel" => channel, "message" => nil}, as: "chat")
{
:noreply,
socket
|> assign(:form, f)
|> put_flash(:info, "Message sent to #{channel}")
}
end
end
I tried this on 1.8.x and 1.7.x and the behavior is the same. What I’m missing here?
Thanks
One temporary solution I can apply: push a custom event event => clean the field on the client side (vanilla JS).
That is working, but it’s not the solution I really wanted.
// assets/js/app.js
window.addEventListener("phx:clear-me", (e) => {
console.log(`cleaning`, e.detail.id);
document.getElementById(e.detail.id).value = "";
});
and:
def handle_event("send", %{"chat" => params}, socket) do
channel = params["channel"]
f = to_form(%{"channel" => channel, "message" => nil}, as: "chat")
{
:noreply,
socket
|> assign(:form, f)
|> push_event("clear-me", %{"id" => "chat_message"})
|> put_flash(:info, "Message sent to #{channel}")
}
end
I found the fix… I need to set temporary_assigns when mounting the view.
LiveView.html#mount/3
So, updated mount looks like this, and voilà, it’s working!
def mount(_params, _session, socket) do
empty = to_form(%{"channel" => "lobby"}, as: "chat")
{:ok, assign(socket, form: empty), temporary_assigns: [form: nil]}
end
Hi @John217Ross , thanks for taking the time to reply.
I tried both ways - reseting to an empty string "" and even nil, and neither of these approaches worked without the temporary_assigns extra argument.
Maybe I’m still missing something.
That’s a spambot, hence the nonsense answer.
The problem is likely that the server cannot override the client input’s value while the input is selected, which is a design choice LiveView makes to avoid race conditions due to server latency while typing.
This has come up many times before on here if you do a search, but I don’t think there is a “blessed” solution yet. One thing you can do is change the id of the input which will cause it to be reset. It is also possible for forms to have a reset button (which still sends an event to the server), so there is probably a way to coerce that into doing what you want. Both of those solutions are hacky, though.
Another solution is to use a JS hook, but I see you’re aware of that. I’m not actually sure why the temporary_assigns trick works.