Subscribe for only $15 to access all of our content

#87: Phoenix LiveView Part 1

Phoenix 1.4

Elixir 1.7

Phoenix LiveView

View source on GitHub


The much anticipated Phoenix LiveView project is now public. In this episode let’s test it out by updating our record application to use Phoenix LiveView.

When we’re finished, we’ll be able to edit our album summaries right from the page and when an album is updated, the changes will be immediately broadcasted to anyone looking at the albums. LiveView is under active development, so there may be changes to the API before the official release.

Let’s get started by configuring our application to use Phoenix LiveView. As of this recording Phoenix LiveView is not on Hex, so we’ll need to get it from GitHub. Let’s open our Mixfile and we’ll phoenix_live_view to our list of dependencies.

mix.exs

...
defp deps do
...
{:phoenix_live_view, github: "phoenixframework/phoenix_live_view"},
...
end
...

Then let’s go to the command line and install it.

$ mix deps.get
...

Then we’ open our dev.exs and we’ll update our Endpoint config to include a signing salt, which Phoenix will use to sign messages between the server and the client. To generate our our signing salt let’s go to the command line and run mix phx.gen.secret 32.

$ mix phx.gen.secret 32
NWuVyBnxDXrRoajCwFlBqIGBjl4bHygi

Then let’s copy it and we’ll include it as the signing salt.

config/dev.exs

...
config :teacher, TeacherWeb.Endpoint,
  http: [port: 4000],
  live_view: [
    signing_salt: "NWuVyBnxDXrRoajCwFlBqIGBjl4bHygi"
  ],
...

We’ll also want to have our LiveView views live reloaded in development. So let’s go to our live_reload section of the config and since by convention live views live in the live directory let’s add an additional pattern to our live_reload config here.

config/dev.exs

...
config :teacher, TeacherWeb.Endpoint,
  live_reload: [
    patterns: [
      ...
      ~r{lib/teacher_web/live/.*(ex)$}
    ]
  ]
...

LiveView also has its own templates, so we’ll need to update our application to handle them. We’ll open our config.exs and update our configuration to include LiveView template engine, which use the extension .leex

config/config.exs

...
config :phoenix,
  template_engines: [leex: Phoenix.LiveView.Engine]
...

Then we’ll need to open our router.ex and add the LiveView.Flash plug after :fetch_flash.

lib/teacher_web/router.ex

...
pipeline :browser do
  plug :accepts, ["html"]
  plug :fetch_session
  plug :fetch_flash
  plug Phoenix.LiveView.Flash
  ...
end
...

Now let’s open our teacher_web.ex module and in the view we’ll import the Phoenix.LiveView.live_render/2 and Phoenix.LiveView.live_render/3 functions. Then in the router we’ll import the Phoenix.LiveView.Router.

lib/teacher_web.ex

...
def view do
  quote do
    ...
    import Phoenix.LiveView, only: [live_render: 2, live_render: 3]
  end
end

def router do
  quote do
    ...
    import Phoenix.LiveView.Router
  end
end
...

We’ll want to expose a LiveView socket. So let’s open our endpoint.ex module and add it.

lib/teacher_web/endpoint.ex

...
socket "/live", Phoenix.LiveView.Socket
...

Then we’ll need to include the Phoenix LiveView NPM dependency. So let’s open our package.json and include it.

assets/package.json

...
"dependencies": {
  "phoenix": "file:../deps/phoenix",
  "phoenix_html": "file:../deps/phoenix_html",
  "phoenix_live_view": "file:../deps/phoenix_live_view"
},
...

Once we’ve added it, let’s go to the command line and cd into our assets directory and then run npm install get our new dependency:

$ cd assets
$ npm install
...
$ cd ../

Now we need to enable connecting to our LiveView socket. To do that we’ll open our app.js and import LiveSocket from phoenix_live_view. Then we’ll assign a new LiveSocket using the “/live” path we created and connect to it. With that our application should be all set up and ready using LiveView. And what’s great is that we’re done writing JavaScript - this is all the JavaScript we’ll need for the whole series.

assets/js/app.js

...
import LiveSocket from "phoenix_live_view"

let liveSocket = new LiveSocket("/live")
liveSocket.connect()

Currently, there are two ways to serve Live Views. Let’s open our router.ex and we can serve a LiveView directly from the router using the live macro, giving it a path and a LiveView to use.

Ore we can render our Live View from our existing controller with LiveView.Controller.live_render. Lets use this method.

We’ll open our album_controller.ex and let’s update our existing index action to use the live_render function. It will take our connection the name of the view. We’ll need to create this, but let’s call it TeacherWeb.AlbumLive.Index Then we can include any session data and let’s remove our call to Recordings.list_albums here. We’ll add it to our LiveView in a minute.

lib/teacher_web/controllers/album_controller.ex

...
def index(conn, _params) do
  LiveView.Controller.live_render(conn, TeacherWeb.AlbumLive.Index, session: %{})
end
...

Now we need to define our LiveView module. Since LiveView modules live in the live directory by convention, let’s create a new directory in teacher_web named live Then let’s create an album_live directory and inside of that we’ll create our index.ex module.

Then we can define our module and LiveView modules use Phoenix.LiveView.

lib/teacher_web/live/album_live/index.ex

defmodule TeacherWeb.AlbumLive.Index do
  use Phoenix.LiveView

end

A Phoenix LiveView module requires two callbacks: mount, which is invoked after a client connects. mount will take any session data, which we’ll ignore since we wont be using it and the socket and render which generates the HTML for the client and takes our socket assigns.

Let’s go back to mount and we need it to return an OK tuple, with the second element being the socket. Now we’ll define what we want our LiveView to render. To start let’s go ahead and define our HTML right in the render function, which we can do with the ~L sigil and let’s just return a simple message here: “Greetings from Live View”.

lib/teacher_web/live/album_live/index.ex

defmodule TeacherWeb.AlbumLive.Index do
  use Phoenix.LiveView

  def mount(_session, socket) do
    {:ok, socket}
  end

  def render(assigns) do
    ~L"""
    Greetings from Live View
    """
  end
end

Now let’s see if everything is working. We’ll start our server.

$ mix phx.server
...

Opening our browser it looks like we have an error - our live_render function is undefined.

Let’s go back to the controller and our LiveView module is missing alias. Let’s add it above with alias Phoenix.LiveView. Since we’re adding an alias for that, let’s go ahead and alias our TeacherWeb.AlbumLive.Index module as well so we can call it without the prefix. Now let’s see if our changes worked.

lib/teacher_web/controllers/album_controller.ex

...
alias Phoenix.LiveView
alias TeacherWeb.AlbumLive.Index

def index(conn, _params) do
  LiveView.Controller.live_render(conn, Index, session: %{})
end
...

We’ll go back to the browser and if we reload the page - it loads and our message is being displayed - “Greetings from Live View.”

Now that we have our Live View working, let’s update it to render our albums.

We’ll go back to our AlbumLive.Index module. Earlier we removed our Recordings.list_albums function from our album_controller.ex. Let’s go ahead and call it here from our mount function and let’s alias our Recordings module so we can call it without the prefix.

Now that we have our albums, we’ll want to add them to our socket assigns. We can use the assign function here to merge data into our socket assigns. With our albums included in the assigns here, they’ll now be available in our render function. Let’s go head and update our render function to render our album’s index.html template. We’ll call AlbumView.render passing in the index.html template and the assigns and let’s also add an alias for our AlbumView so we can call it without the prefix.

lib/teacher_web/live/album_live/index.ex

defmodule TeacherWeb.AlbumLive.Index do
  use Phoenix.LiveView

  alias Teacher.Recordings
  alias TeacherWeb.AlbumView

  def mount(_session, socket) do
    albums = Recordings.list_albums()
    {:ok, assign(socket, albums: albums)}
  end

  def render(assigns) do
    AlbumView.render("index.html", assigns)
  end

end

Now earlier when we were setting up our application to use LiveView, we added support for LiveView templates with the .leex extension.

We’ll need to rename the template we’re trying to render to use that extension.

Let’s find our template and rename it index.html.leex.

Then let’s open our template and we have a link to our album show path that uses the @conn. Let’s go ahead and update it to use our @socket since @conn will no longer be available.

Template path: lib/teacher_web/templates/album/index.html.leex

...
<%= link "Show", to: Routes.album_path(@socket, :show, album) %>
...

Then let’s go back to our browser - and perfect our albums are now being rendered with LiveView!

More Episodes

Alchemist's Edition

#104: Generating RSS Feeds with Elixir

In this episode we’ll take an existing Elixir Phoenix application and build an Atom feed for it. To build the feed we’ll use the Atomex package.

Watch episode

#103: Writing Tests with ExVCR

In this episode we’ll explore one way of mocking external HTTP requests when testing using ExVCR. ExVCR records the actual HTTP request and response and replays it whenever that test runs.

Watch episode
Alchemist's Edition

#102: OTP backed Web Application Part 4

In the final part of this series we’ll use Phoenix Channels to improve the UI so that our coin tracker page updates automatically when a new cryptocurrency price is found.

Watch episode