Phoenix LiveView has been an exciting recent addition to Elixir/Phoenix ecosystem. In this article, I will provide overview of Phoenix LiveView and some of its salient features followed by an example CRUD application developed using Phoenix Framework 1.4 and LiveView.

What is Phoenix LiveView?

As described in the article here — “Phoenix LiveView is an exciting new library which enables rich, real-time user experiences with server-rendered HTML. LiveView powered applications are stateful on the server with bidrectional communication via WebSockets, offering a vastly simplified programming model compared to JavaScript alternatives.”

If you don’t like Javascript or don’t want to write much of Javascript, Phoenix LiveView provides a viable and in my opinion better alternative for writing real-time applications with server-rendered HTML.

Phoenix LiveView Salient Features:

You can read many articles on Phoenix LiveView and get a sense of what it’s capable of doing on the internet, but in summary —

LiveView provides simpler programming model for writing real-time interactive web applications with server-rendered HTML

LiveView connection process is as below —

It’s a two step connection process —

Step 1, 2, 3 in above diagram denotes the stateless connection where the entire static and dynamic HTML content is sent to browser from server and get rendered.

Step 4, 5 establishes a stateful connection via WebSocket and futher updates are sent for only the dynamically changed contents. LiveEEx (by José Valim) is used to compute the parts that have changed and only the minimal updated parts are sent to browser.

If you want to understand more on how this happens internally, please check here and here for details.

However, client side applications are not going away as mentioned in the article here, specially, if you need offline capability.

Developing a CRUD Application using LiveView:

In this section, I will walk through an example CRUD application developed using LiveView. The example application has been extracted from the Phoenix LiveView Example Repository here — https://github.com/chrismccord/phoenix_live_view_example but it’s been simplified and adapted to fit this article. You can find many other examples using LiveView at the above github repository.

Complete application code can be found on my github here — https://github.com/imeraj/Phoenix_Playground/tree/master/1.4/user_liveview

Application Overview:

The application is a simple CRUD application with one User schema, some validations on the schema, and with operations — add new user, list users, show user, update user, and delete user. I added necessary code to perform real-time validation using LiveView.

Step 1: Generate the Phoenix Application

Generate a Phoenix application with mysql database using the command below —

mix phx.new user_liveview — database mysql

Step 2: Define Schema and Add Validation

Define a User schema using the command below —

mix phx.gen.html Accounts User users name:string email:string phone_number:string

Generated User schema with validation looks as below —

defmodule UserLiveview.Accounts.User do

use Ecto.Schema

import Ecto.Changeset



schema "users" do

field :email, :string

field :name, :string

field :phone_number, :string



timestamps()

end



@phone ~r/^(\+\d{1,2}\s)?\(?\d{3}\)?[\s.-]\d{3}[\s.-]\d{4}$/

@email ~r/\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i



@doc false

def changeset(user, attrs) do

user

|> cast(attrs, [:name, :email, :phone_number])

|> validate_required([:name, :email, :phone_number])

|> validate_format(:name, ~r/^[a-zA-Z0-9_]*$/,

message: "only letters, numbers, and underscores please"

)

|> validate_length(:name, max: 12)

|> validate_format(:email, @email, message: "must be a valid email address")

|> validate_format(:phone_number, @phone, message: "must be a valid number")

|> unique_constraint(:email)

end

end

Step 3: Integrate Phoenix LiveView

in mix.exs: add below dependency —

{:phoenix_live_view, github: “phoenixframework/phoenix_live_view”}

in config.exs: add the below signing salt —

# Configures the endpoint

config :user_liveview, UserLiveviewWeb.Endpoint,

url: [host: "localhost"],

secret_key_base: "jUaYyObybTnU5twQ7xzNA2+cFLkHyNY/I+FMe7wqh8sguXdKryceqWXT+zTrm4yI",

render_errors: [view: UserLiveviewWeb.ErrorView, accepts: ~w(html json)],

pubsub: [name: UserLiveview.PubSub, adapter: Phoenix.PubSub.PG2],

live_view: [

signing_salt: "KK4ly8HdzOmhsB+2itWau5xYDIPW8ctk"

]

in router.ex: add LiveView flash plug after :fetch_flash —

pipeline :browser do

plug :accepts, ["html"]

plug :fetch_session

plug :fetch_flash

plug Phoenix.LiveView.Flash

plug :protect_from_forgery

plug :put_secure_browser_headers

end

in user_liveview_web.ex: add below imports —

def view do

quote do

use Phoenix.View,

root: "lib/user_liveview_web/templates",

namespace: UserLiveviewWeb



# Import convenience functions from controllers

import Phoenix.Controller, only: [get_flash: 1, get_flash: 2, view_module: 1]



import Phoenix.LiveView, only: [live_render: 2, live_render: 3]



# Use all HTML functionality (forms, tags, etc)

use Phoenix.HTML



import UserLiveviewWeb.ErrorHelpers

import UserLiveviewWeb.Gettext

alias UserLiveviewWeb.Router.Helpers, as: Routes

end

end



def router do

quote do

use Phoenix.Router

import Plug.Conn

import Phoenix.Controller



import Phoenix.LiveView.Router

end

end

in endpoint.ex: add LiveView socket information —

defmodule UserLiveviewWeb.Endpoint do

use Phoenix.Endpoint, otp_app: :user_liveview



socket "/live", Phoenix.LiveView.Socket



...

end

in package.json: add LiveView NPM dependencies —

"dependencies": {

"phoenix": "file:../deps/phoenix",

"phoenix_html": "file:../deps/phoenix_html",

"phoenix_live_view": "file:../deps/phoenix_live_view"

},

For installing NPM dependencies — cd assets && npm install

in app.js: add connecting ability to LiveView socket —

import LiveSocket from "phoenix_live_view"



let liveSocket = new LiveSocket("/live")

liveSocket.connect()

in dev.exs: add live page reload support —

config :user_liveview, UserLiveviewWeb.Endpoint,

live_reload: [

patterns: [

~r{priv/static/.*(js|css|png|jpeg|jpg|gif|svg)$},

~r{priv/gettext/.*(po)$},

~r{lib/user_liveview_web/views/.*(ex)$},

~r{lib/user_liveview_web/templates/.*(eex)$},

~r{lib/user_liveview_web/live/.*(ex)$}

]

]

in app.css: add (optionally) LiveView CSS —

@import "../../deps/phoenix_live_view/assets/css/live_view.css";

At this stage, LiveView integration is done with application.

LiveView code will reside under — lib/user_liveview_web/live folder. Since it does not exist, need to create it.

Step 4: Add LiveView Routes

Modify router.ex and specify the routes as below —

scope "/", UserLiveviewWeb do

pipe_through :browser



live "/", UserLive.Index

live "/users", UserLive.Index

live "/users/new", UserLive.New

live "/users/:id", UserLive.Show

live "/users/:id/edit", UserLive.Edit

end

Step 5: Add Necessary LiveView Code

Under live/user directory, add edit.ex, index.ex, new.ex and show.ex (https://github.com/imeraj/Phoenix_Playground/tree/master/1.4/user_liveview/lib/user_liveview_web/live/user)

Under templates/user directory, add necessary LiveView templates (with extensions .leex) — edit.html.leex, form.html.leex, index.html.leex, new.html.leex, show.html.leex (https://github.com/imeraj/Phoenix_Playground/tree/master/1.4/user_liveview/lib/user_liveview_web/templates/user)

Update user_view.ex as below —

defmodule UserLiveviewWeb.UserView do

use UserLiveviewWeb, :view



alias UserLiveviewWeb.UserLive

end

At this stage, run the application using below commands —

mix deps.get

mix ecto.setup

mix phx.server

Launch your browser and hit localhost:4000 and you should see the below screen. If you have your browser’s WebInspector open, you should see a join event (to LiveView WebSocket) as shown below on the right side —