Elixir

Integrating Phoenix LiveView

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:

You've successfully subscribed to SmartLogic Blog
Great! Next, complete checkout for full access to SmartLogic Blog
Welcome back! You've successfully signed in.
Unable to sign you in. Please try again.
Success! Your account is fully activated, you now have access to all content.
Error! Stripe checkout failed.
Success! Your billing info is updated.
Error! Billing info update failed.