Phoenix & LiveView Cheatsheet
Quick reference for Phoenix web framework and LiveView — routing, controllers, contexts, LiveView, and components.
Project Setup
| Command |
Description |
mix phx.new my_app |
Create new Phoenix project |
mix phx.new my_app --no-ecto |
Without database |
mix phx.new my_app --no-html |
API-only project |
mix phx.new my_app --live |
With LiveView (default since 1.6) |
mix phx.new my_app --umbrella |
Umbrella project |
mix phx.server |
Start dev server |
iex -S mix phx.server |
Start server with IEx |
Generators
| Command |
Description |
mix phx.gen.html Blog Post posts title:string body:text |
HTML scaffold |
mix phx.gen.json Blog Post posts title:string body:text |
JSON scaffold |
mix phx.gen.live Blog Post posts title:string body:text |
LiveView scaffold |
mix phx.gen.schema Blog Post posts title:string body:text |
Schema + migration only |
mix phx.gen.context Blog Post posts title:string body:text |
Context + schema only |
mix phx.gen.auth |
Authentication system |
mix phx.routes |
Show all routes |
Routing
# lib/my_app_web/router.ex
scope "/", MyAppWeb do
pipe_through :browser
get "/", PageController, :index
get "/about", PageController, :about
resources "/posts", PostController
live "/dashboard", DashboardLive
live "/users/:id", UserLive.Show
live "/posts", PostLive.Index
live "/posts/new", PostLive.New
live "/posts/:id/edit", PostLive.Edit
end
# API scope
scope "/api", MyAppWeb.API do
pipe_through :api
resources "/posts", PostController, except: [:new, :edit]
end
Controller
defmodule MyAppWeb.PostController do
use MyAppWeb, :controller
def index(conn, _params) do
posts = Blog.list_posts()
render(conn, :index, posts: posts)
end
def show(conn, %{"id" => id}) do
post = Blog.get_post!(id)
render(conn, :show, post: post)
end
def create(conn, %{"post" => post_params}) do
case Blog.create_post(post_params) do
{:ok, post} ->
conn
|> put_flash(:info, "Post created.")
|> redirect(to: ~p"/posts/#{post}")
{:error, changeset} ->
render(conn, :new, changeset: changeset)
end
end
def delete(conn, %{"id" => id}) do
post = Blog.get_post!(id)
{:ok, _} = Blog.delete_post(post)
conn
|> put_flash(:info, "Post deleted.")
|> redirect(to: ~p"/posts")
end
end
LiveView
defmodule MyAppWeb.DashboardLive do
use MyAppWeb, :live_view
@impl true
def mount(_params, _session, socket) do
{:ok, assign(socket, count: 0, items: [])}
end
@impl true
def handle_event("increment", _, socket) do
{:noreply, update(socket, :count, &(&1 + 1))}
end
@impl true
def handle_event("add_item", %{"name" => name}, socket) do
{:noreply, update(socket, :items, &[name | &1])}
end
@impl true
def render(assigns) do
~H"""
<div>
<h1>Count: <%= @count %></h1>
<button phx-click="increment">+</button>
<.simple_form for={@item_form} phx-submit="add_item">
<.input field={@item_form[:name]} />
<:actions><.button>Add</.button></:actions>
</.simple_form>
<ul>
<%= for item <- @items do %>
<li><%= item %></li>
<% end %>
</ul>
</div>
"""
end
end
LiveView Events & Bindings
| Attribute |
Description |
phx-click |
Click event |
phx-submit |
Form submit event |
phx-change |
Form input change event |
phx-blur |
Blur event |
phx-focus |
Focus event |
phx-keydown |
Keydown event |
phx-keyup |
Keyup event |
phx-hook |
Attach a JS hook |
phx-debounce |
Debounce events (ms or "blur") |
phx-throttle |
Throttle events (ms) |
phx-disable-with |
Disable element during request |
phx-update |
DOM merge strategy |
Verifying Routes (sigil ~p)
# Routes are verified at compile time
~p"/posts" # => "/posts"
~p"/posts/#{post}" # => "/posts/42"
~p"/posts?page=2" # => "/posts?page=2"
Ecto Quick Reference
# Schema
schema "posts" do
field :title, :string
field :body, :text
field :views, :integer, default: 0
belongs_to :user, MyApp.Accounts.User
has_many :comments, MyApp.Blog.Comment
timestamps()
end
# Changeset
def changeset(post, attrs) do
post
|> cast(attrs, [:title, :body, :user_id])
|> validate_required([:title, :body])
|> validate_length(:title, min: 3, max: 200)
end
# Queries
import Ecto.Query
Post |> where([p], p.views > 100) |> Repo.all()
Post |> order_by(desc: :inserted_at) |> limit(10) |> Repo.all()
Post |> where(user_id: ^user_id) |> Repo.all()
Repo.get(Post, 42)
Repo.get_by!(Post, slug: "hello-world")
Repo.insert(%Post{title: "Hi"})
Repo.update(post_changeset)
Repo.delete(post)