The blog ofJonathan Pepin

Elixir Phoenix LiveView with a real world example

2019-03-20

Chris McCord recently released the code for the new Phoenix LiveView, which allows you to create interactive, real time templates, using server-rendered HTML - In other words, no Javascript needed!

There's been a few examples floating here and there, and Chris added a few useful examples on his Github, but I haven't seen much real world examples yet. And since I migrated the sandbox on joeschmoe.io to use it, I felt like it'd be interesting to share a real example!

The current sandbox

Joe Schmoe is an free illustrated avatar collection that you can use for your projects. It has a simple API you can query to get Schmoes. To show off how it works, the website has a sandbox, which is a simple text field in which you can enter any string you want, and it'll show you which Schmoe would be returned.

Simple and straight forward enough, so it was originally built with Javascript.

See the Pen Joe Schmoe Sandbox by Jonathan Pepin (@Jauny) on CodePen.

On the server side, it has a get '/' routing to our PageController which renders the index.html.eex template.

Migrating to LiveView

I'm not seeing LiveView replacing full SPA and very complex UIs quite yet, but it is perfect for this type of logic - replacing bits of JS here and there for interactivity, AJAX, etc.

It was simple, straight forward, and cleaned quite a bit of code, while making everything simpler.

LiveViews are still in beta and have not been merged into Phoenix yet, so there is a one-time setup you have to do to add LiveView functionality to your Phoenix app. I won't repeat it here, you can head up to the official doc for that.

Once your project is setup, we can start migrating our "normal" view into a LiveView. There are a few approaches we can take, so let's first see what we want to do here exactly.

Some LiveViews can be rendered fully on their own, but here what we really want, is create a LiveView for our sandbox, and have it embedded into our index page.

For this, we need:

And that's pretty much it actually!

Creating our LiveView

By best practices, live views live in lib/app_web/live/ so let's go ahead and create sandbox_live.ex in this new folder.

defmodule AppWeb.SandboxLive do
  use Phoenix.LiveView
  alias AppWeb.PageView

  def render(assigns) do
    ~L"""
    <div class="sandbox">
      <div>
        <form>
          <input name="q" placeholder="Enter a name"></input>
        </form>
      </div>
      <div>
        <img src="https://joeschmoe.io/api/v1/<%= @val %>" />
      </div>
    </div>
    """
  end

  def mount(_session, socket) do
    {:ok, assign(socket, val: "random")}
  end

  def handle_event("update-avatar", %{"q" => name}, socket) do
    case name do
      "" -> {:noreply, assign(socket, :val, "random")}
      _  -> {:noreply, assign(socket, :val, name)}
    end
  end
end

The render function is used to set what is getting rendered as part of the live view. Here, the template code is short enough that we decide to render it directly using the new ~L sigil, used to represent leex (aka live eex).

If we wanted to render a .leex template instead, we could call LiveView.Controller.live_render/3, which works the same way as the normal render/3 function from controllers.

You can see that the HTML rendered is the same as the one from the original template, but has all the Javascript-related code removed, and instead uses <%= @val %> to interpolate what is now a server variable.

This variable is bound to the server by the new phx-change="update-avatar" attribute added to our form, which will send the "update-avatar" event to our LiveView module when the form changes.

The next function in our LiveView is mount. It is called as the client opens a web socket connection with the server, when the page first renders. This is when the @val value is injected and bound to the HTML.

Finally, we have the handle_event function which is fired when an event is sent through the web socket.

The new phx-change="update-avatar" attribute on our form is one example of how to trigger events. Every time the form is updated, an event named "update-avatar", along with the form data is sent to our LiveView and caught by the handle_event function.

We pattern match to assign the value of our input named "q" to the variable name and bind this value to @val, which then gets updated into the HTML and updates the image.

Updating our template

Now that we have our LiveView.ex setup and ready to render leex and receive events from it, let's actually have it rendered into our page.

First, we need to rename our index.html.eex to index.html.leex - note that we are updated the file type from .eex to .leex. This makes our template a live view template, and give it access to Phoenix.LiveView functions - such as live_render.

Then, we need to remove the original sandbox HTML and Javascript and replace it with our SanboxLiveView HTML instead - for this, we use the live_render function, newly made available to our template by making it a live eex template.

<div class="main">
  <h1>Welcome to Joe Schmoe!</h1>
  <%= live_render(@conn, AvatarWeb.SandboxLiveView) %>
  <div class="footer">Good bye!</div>
</div>

The render function from our LiveView will get called and the ~L will get injected.

All done!

As you can see, after a little bit of setup, using Live Views instead of Javascript for interactive parts of our HTML pages allows for a cleaner code. It is used in the real world on the actual sandbox of Joe Schmoe, and it works great!