Modern Talking with Elixir: Messenger App Tutorial with Phoenix LiveView

Modern Talking with Elixir: Messenger App Tutorial with Phoenix LiveView

Have you ever wondered why Elixir and its ecosystem is gradually becoming the go-to platform for many web application developers who want both performance and productivity, not a tradeoff between them?

Well, we'll show you why - and we'll do it via a thorough crash course of Phoenix Framework's hot deal, Phoenix LiveView, using which we'll build a Messenger-like live chat application.

As of November 2020, the latest Phoenix LiveView version is 0.14.8 - and the series has been updated to match it!

Table of contents

    In a series of articles (don't forget to subscribe to our newsletter!), we'll convince you that Phoenix LiveView will revolutionize the way you create reactive UIs!

    Why Phoenix?

    The thing about Elixir, on top of Erlang/OTP, is that it offers a great mix of making life easy and being a scalable and reliable platform that will not let you down when your estimated traffic of 4000 users becomes 4,000,000 users.

    Phoenix Framework is Elixir's answer to the never-ending question of how to build rich web applications, and it's got a lot of tools that make the job easy - one of the latest being Phoenix LiveView.

    Long story short, LiveView is a tool that lets an Elixir developer create reactive UIs without writing a single line of JS code. Which is great, given that many Elixir developers do not exactly consider themselves fluent at JS, or - just like myself - are not exactly in love with JS.

    Lessons learnt from reactive UI libraries

    Many JavaScript frameworks, both contemporary and not-so-contemporary ones, rely on manipulating the page's DOM for dynamic content updates.

    Historically, for instance, developers using BackboneJS would define a Backbone.View to represent an atomic chunk of user interface, behind which there's a Backbone.Model, encapsulating the business logic of data.

    Backbone remained unopinionated about how views were to be rendered, so it had no built-in tools to make the re-rendering of views on model changes efficient - the whole structure of a view had to be built from scratch and replaced, which tended to yield inefficient views.

    In contrast, modern frameworks such as ReactJS or Vue.js don't care about how the data model layer works at all (loosely coupled data stores such as Redux are often used for this) - but they have a virtual DOM concept - long story short, a pattern of incrementally upgrading only those elements that need to be changed, based on changes in the state of particular components and their children.

    The challenge, though, is pretty much down to how to exchange data between the UI and the backend. You will usually need to implement a JSON API or a GraphQL service, or perhaps you could develop a WebSocket-based solution using Phoenix Channels.

    Either way, the Pareto 80/20 principle will imminently catch you, and when you get to the 20% of work needed to finish off your message-passing code, it'll soon become a framework within a framework.

    Why, where & how LiveView excels

    Phoenix LiveView's concept is both groundbreaking and familiar, in different ways.

    It is familiar in that it lets you define UI elements as nestable components composed of pure HTML markup, and it builds upon the experience of reactive UI frameworks in implementing mechanisms that calculate diffs between consecutive UI states to ensure efficient updates.

    It is groundbreaking in the way it maintains the states of components and manages their updates - in Phoenix LiveView, components are stateful on the server, and their events and updates are communicated via a bidirectional WebSocket connection.

    Phoenix LiveView is built on top of Elixir processes and Phoenix Channels - every LiveView instance is a BEAM process, acting very much like a GenServer, receiving messages and updating its state.

    While modern JS frameworks such as React have server-side rendering capabilities, it is usually not convenient to do this in a non-NodeJS backend server. Rendering content via JavaScript often results in SEO issues, and some trickery is needed for search engines to index the page correctly. In Phoenix LiveView, the initial render is static as in the classic HTML request-response cycle, so you'll get good Lighthouse scores and it won't hurt your SEO.

    Erlang easily maintains thousands of processes concurrently, and Phoenix authors have even managed to make it handle 2 million WebSocket connections on a single (albeit pretty strong) machine. With the server using Elixir's strengths to manage LiveView states, the client-side logic can be thin and simple.

    In fact, as stated in the introduction, in most LiveView-powered apps you won't write a single line of JS code. In many cases, when interacting with an element whose update is supposed to fetch data for a new UI state from the server, the workflow using a reactive JS framework would be:

    1. Handle the element's change event
    2. Send a request to the server containing the actual changes
    3. Receive response and update state store based on response data
    4. Let the view layer re-render the changed DOM elements

    This involves annotating HTML elements so that they can be identified by JS code, writing browser-side scripts to handle the element's state change event, send a payload to the server, which processes the request as part of a Phoenix controller action.

    With Phoenix LiveView, you only write HTML and Elixir code, with the JS part being handled by a script bundled with the LiveView package.

    Phoenix LiveView basic usage

    The basic idea behind Phoenix LiveView is very simple and straightforward. Be sure to subscribe to our newsletter to learn more!

    LiveView is an Elixir behaviour, and your most basic LiveView definition will consist of two callback implementations:

    • A render/1 function, containing the template of how your component is represented in HTML, with elements of the component's state interpolated. This is much like defining an ordinary view. The special ~L sigil is used to interpolate assigns into your EEx syntax, and convert it into an HTML-safe structure.
    • A mount/2 function, wiring up socket assigns and establishing the LiveView's initial state.
      defmodule YourappWeb.CounterLive do
        use Phoenix.LiveView
    
        def render(assigns) do
          ~L"""
          <a href='#' phx-click='increment'>
            I was clicked <%= @counter %> times!
          </a>
          """
        end
    
        def mount(params, socket) do
          {:ok, assign(socket, :counter, 0)}
        end
      end

    However, the whole fun of using LiveView is managing its state, and the next two callbacks will come in handy.

    • A handle_event/3 function, handling events coming from the browser. Noticed the phx-click attribute in our template's link? This is the name of an event that will be transported to the LiveView process via WebSockets. We'll define a function clause that will match to the event's name.
      def handle_event("increment", params, %{assigns: %{counter: counter}} = socket) do
        {:noreply, assign(socket, :counter, counter + 1)}
      end

    It will mutate the LiveView's state to have a new, incremented value of the counter, and the render/1 function will be called with the new assigns.

    The second argument, here named params, is of special interest as well, because - in the case of a phx-click event - it contains the event's metadata:

      %{
        "altKey" => false,
        "ctrlKey" => false,
        "metaKey" => false,
        "pageX" => 399,
        "pageY" => 197,
        "screenX" => 399,
        "screenY" => 558,
        "shiftKey" => false,
        "x" => 399,
        "y" => 197
      }

    We trust that you won't now hesitate to try it out with a <form> tag and a phx-change attribute to see what event metadata are passed when a form element's value is changed. Either way, we'll explore this in more detail in later episodes of this tutorial - stay tuned and subscribe to our newsletter so that you don't miss out!

    • A handle_info/2 callback, handling events coming from anywhere but the browser. This means events sent from external sources (remember a LiveView is just an Elixir process, so you can do whatever's needed in order for it to receive messages!), or events sent from the LiveView to itself. For instance, it takes this to increment the counter every 5 seconds:
      def mount(params, socket) do
        if connected?(socket), do: :timer.send_interval(5000, self(), :increment)
    
        {:ok, assign(socket, :counter, 0)}
      end
    
      def handle_info(:increment, %{assigns: %{counter: counter}} = socket) do
        {:noreply, socket |> assign(:counter, counter + 1)}
      end

    To reduce code repetition, you could make handle_event/3 send a message to self() that triggers the same handle_info/2 routine.

    You can now access your LiveView as a standalone route - to do this, put this in your router.ex:

    import Phoenix.LiveView.Router
    
    scope "/", YourappWeb do
     live "/counter", CounterLive
    end

    ...or render the LiveView within any other template:

    <%= Phoenix.LiveView.live_render(@conn, YourappWeb.CounterLive) %>

    The Curious Messenger Roadmap

    We'll make you familiar with how to wield the Phoenix LiveView sword, and you'll build a fully-fledged Messenger replacement, which will make you (almost) forget about any other instant messaging app you had ever used before...

    Phoenix LiveView is obviously only part of the story, and there's a lot more ground that we'll cover. We'll do a few episodes, each of which touches a different set of concerns that we'll have to consider when designing the app.

    Keep #BusyBeingCurious - hopefully you weren't too bored with our introductory talk. The real thing is coming very soon - stay tuned and subscribe!

    Check out other articles in this series:

    Michał Buszkiewicz, Elixir Developer
    Michał Buszkiewicz Curiosum Founder & CTO

    Read more
    on #curiosum blog

    Phoenix LiveView Tutorial: Bootstrap Your Messenger App

    Phoenix LiveView Tutorial: Bootstrap Your Messenger App

    We're back with the first practical part of our **Modern Talking with Elixir** series, and this time we'll get the initial setup for our Phoenix LiveView-based Messenger app up and running. We'll prepare our app for Phoenix LiveView and install all needed dependencies, design the app's Ecto schemas, related contexts, and database structure, to accommodate for the app's business logic. **As of November 2020, the latest Phoenix LiveView version is 0.14.8 - and the series has been updated to match it!**
    Phoenix LiveView Tutorial: Adding Phoenix PubSub and Pow Authentication to Messenger

    Phoenix LiveView Tutorial: Adding Phoenix PubSub and Pow Authentication to Messenger

    We've already bootstrapped our Phoenix LiveView-based Messenger app's database structure and a first LiveView page.

    This time, we're going to improve real-time communication between the app's users using Phoenix PubSub, and use the Pow library to add secure user authentication.

    As of November 2020, the latest Phoenix LiveView version is 0.14.8 - and the series has been updated to match it!