Phoenix LiveView came out in a preview form at the end of last week, so for this week's SmartLogic TV live stream I wanted to integrate it into an existing project. Before the start of the stream I hadn't looked at any of the examples or docs on how to set it up; I had only seen Chris McCord's keynotes showing it off at ElixirConf and Lonestar Elixir.
One note before beginning on your own LiveView journey: it is still in development and you shouldn't base your whole app on it just yet.
Starting Point
For some context, I have a side project called Grapevine that lets you play old-school telnet-based multiplayer text games called MUDs. You can play these via a web client that connects via Phoenix channels back to an internal telnet client process which is actually on a separate BEAM node, to further complicate matters.
I have a simple admin dashboard that displays active web client connections, but it is static and does not auto update as they are opened or closed. Sounds like the perfect thing for a live view extension!
Integrating LiveView
To integrate LiveView I followed the README, which sets up the application for rendering live views.
In my EEx template I replaced the snippet that I wanted to turn "live" with this:
<%= live_render(@conn, OpenWebClientView, session: [clients: @clients]) %>
I mostly copied the full html into a new LiveView view.
defmodule OpenWebViewClient do
use Web, :live
alias Telnet.Presence
def mount(_session, socket) do
Web.Endpoint.subscribe("telnet:presence")
socket = assign(socket, :clients, Presence.online_clients())
{:ok, socket}
end
def render(assigns) do
~L[
<h4>Open Web Clients</h4>
...
]
end
end
The view also subscribes to an internal-only Phoenix Channel named telnet:presence
that the web clients will be pushing notices over.
Receiving Messages
The last piece that makes this work is the handle_info
function that receives the broadcasts:
def handle_info(broadcast = %{topic: "telnet:presence"}, socket) do
case broadcast.event do
"client/online" ->
client_online(socket, broadcast.payload)
"client/offline" ->
client_offline(socket, broadcast.payload)
"client/update" ->
client_update(socket, broadcast.payload)
end
end
def client_online(socket, client) do
socket = assign(socket, :clients, [client | socket.assigns.clients])
{:noreply, socket}
end
The magic happens anytime assigns/3
changes socket.assigns
. The live view updates with the new values and re-renders automatically. Dynamic pages with no custom javascript.
See the other client functions on GitHub.
Conclusion
I was very impressed with how easy Phoenix LiveView is to set up. It was also fun to see how even when the return values for handle_info
were off, I still got a correct display because the backing process crashed and simply restarted to a good state.
See the full code on GitHub:
Watch me code this whole example "live" on YouTube: