I have a live view, which, among other things, repeatedly reads a timer running on the server and updates the page with the timer’s value. The same view also has a one-field form. The form is unrelated to the timer, and the timer display markup is not part of the form. I noticed that once the timer reads start, whatever a user tries to type in the text input field of the form is deleted when the timer updates. Because I am reading the timer very frequently, it is becomes impossible to submit a value because anything entered in the field is wiped before a submit button can be pressed.
If I disable the timer update, everything is fine.
I wonder if somehow the form part of the page is getting included in a diff even though only the timer part is changing, and I just misunderstand how Live View selectively updates parts of the page, or if the way that I am broadcasting changes to several users is the problem, (or something dumber). As such I’ve included the relevant code for both aspects of the update cycle below. Thank you everyone in advance for any help you can provide!
First, when the view is mounted it subscribes to a PubSub topic in order to push changes to multiple users (it’s an auction application, so everyone needs to see when someone submits a bid, for example).
def mount(session, socket) do
if connected?(socket) do
Phoenix.PubSub.subscribe(AuctionRoom.PubSub, "room:1")
end
{:ok, assign(socket, ...., timer: "Not Started")}
end
When an auction is started, the timer starts on the server, and we read it every 100 milliseconds:
def handle_event("new_auction", _value, socket) do
RoomServer.new_auction(socket.assigns.room_pid) # -> starts timer on the server
if connected?(socket) do
:timer.send_interval(100, self(), :read_timer)
end
broadcast_socket(socket, [status: "started"])
end
def handle_info(:read_timer, socket) do
timer = RoomServer.read_timer(socket.assigns.room_pid)
broadcast_socket(socket, [timer: timer])
end
broadcast_socket/2
publishes a :room_update
event to the PubSub topic subscribers, and that event handler updates the view.
defp broadcast_socket(socket, assigns) do
Phoenix.PubSub.broadcast!(AuctionRoom.PubSub, "room:1", {:room_update, assigns})
{:noreply, socket}
end
def handle_info({:room_update, assigns}, socket) do
{:noreply, assign(socket, assigns)}
end
The applicable part of the template looks like so:
<div id="timer_container">Time: <%= @timer %></div>
…
<form phx-submit="bid_custom">
<input id="bid_amount" name="bid_amount" type="number">
<button id="submit_custom" type="submit">Bid</button>
</form>