Loading...
Loading...
Render views and templates in Phoenix using HEEx templates, function components, slots, and assigns
npx skill4agent add thebushidocollective/han phoenix-views-templatesembed_templatesdefmodule HelloWeb.HelloHTML do
use HelloWeb, :html
embed_templates "hello_html/*"
end.html.heexhello_html/<section>
<h2>Hello World, from Phoenix!</h2>
</section><%= ... %><section>
<h2>Hello World, from <%= @messenger %>!</h2>
</section>@=<% # This is a comment %>
<% user_name = String.upcase(@user.name) %>
<p>Welcome, <%= user_name %>!</p># Controller
def show(conn, %{"messenger" => messenger}) do
render(conn, :show, messenger: messenger, receiver: "Dweezil")
end<!-- Template -->
<section>
<h2>Hello <%= @receiver %>, from <%= @messenger %>!</h2>
</section>@if/else<%= if some_condition? do %>
<p>Some condition is true for user: <%= @username %></p>
<% else %>
<p>Some condition is false for user: <%= @username %></p>
<% end %><%= unless @user.premium do %>
<div class="upgrade-banner">
Upgrade to premium for more features!
</div>
<% end %><%= case @status do %>
<% :pending -> %>
<span class="badge badge-warning">Pending</span>
<% :approved -> %>
<span class="badge badge-success">Approved</span>
<% :rejected -> %>
<span class="badge badge-danger">Rejected</span>
<% end %>for<table>
<tr>
<th>Number</th>
<th>Power</th>
</tr>
<%= for number <- 1..10 do %>
<tr>
<td><%= number %></td>
<td><%= number * number %></td>
</tr>
<% end %>
</table><ul>
<%= for post <- @posts do %>
<li>
<h3><%= post.title %></h3>
<p><%= post.excerpt %></p>
</li>
<% end %>
</ul><ul>
<li :for={item <- @items}><%= item.name %></li>
</ul>Enum.with_index/2<%= for {item, index} <- Enum.with_index(@items) do %>
<div class="item-<%= index %>">
<%= item.name %>
</div>
<% end %>attr~Hdefmodule HelloWeb.HelloHTML do
use HelloWeb, :html
embed_templates "hello_html/*"
attr :messenger, :string, required: true
def greet(assigns) do
~H"""
<h2>Hello World, from <%= @messenger %>!</h2>
"""
end
end<.component_name /><section>
<.greet messenger={@messenger} />
</section>attr :messenger, :string, default: nil
attr :class, :string, default: "greeting"
def greet(assigns) do
~H"""
<h2 class={@class}>
Hello World<%= if @messenger, do: ", from #{@messenger}" %>!
</h2>
"""
endattr :title, :string, required: true
attr :count, :integer, default: 0
attr :active, :boolean, default: false
attr :user, :map, required: true
attr :items, :list, default: []
def card(assigns) do
~H"""
<div class={"card" <> if @active, do: " active", else: ""}>
<h3><%= @title %></h3>
<p>Count: <%= @count %></p>
<p>User: <%= @user.name %></p>
<ul>
<li :for={item <- @items}><%= item %></li>
</ul>
</div>
"""
endassign/2attr :x, :integer, required: true
attr :y, :integer, required: true
attr :title, :string, required: true
def sum_component(assigns) do
assigns = assign(assigns, sum: assigns.x + assigns.y)
~H"""
<h1><%= @title %></h1>
<p>Sum: <%= @sum %></p>
"""
endslot :inner_block, required: true
def card(assigns) do
~H"""
<div class="card">
<%= render_slot(@inner_block) %>
</div>
"""
end<.card>
<h2>Card Title</h2>
<p>Card content goes here</p>
</.card>slot :header, required: true
slot :body, required: true
slot :footer
def panel(assigns) do
~H"""
<div class="panel">
<div class="panel-header">
<%= render_slot(@header) %>
</div>
<div class="panel-body">
<%= render_slot(@body) %>
</div>
<%= if @footer != [] do %>
<div class="panel-footer">
<%= render_slot(@footer) %>
</div>
<% end %>
</div>
"""
end<.panel>
<:header>
<h2>Panel Title</h2>
</:header>
<:body>
<p>Panel content</p>
</:body>
<:footer>
<button>Close</button>
</:footer>
</.panel>slot :item, required: true do
attr :title, :string, required: true
attr :highlighted, :boolean, default: false
end
def list(assigns) do
~H"""
<ul>
<%= for item <- @item do %>
<li class={if item.highlighted, do: "highlight"}>
<%= item.title %>: <%= render_slot(item) %>
</li>
<% end %>
</ul>
"""
end<%= render("child_template.html", assigns) %><MyApp.Components.button text="Click me" />alias MyApp.Components
# In template:
<Components.button text="Click me" />def controller do
quote do
use Phoenix.Controller,
formats: [:html, :json],
layouts: [html: HelloWeb.Layouts]
...
end
end@inner_content<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<title>My App</title>
</head>
<body>
<%= @inner_content %>
</body>
</html><Layouts.app flash={@flash}>
<section>
<h2>Hello World, from <%= @messenger %>!</h2>
</section>
</Layouts.app>def home(conn, _params) do
render(conn, :home, layout: false)
enddefmodule AppWeb.ThermostatLive do
use Phoenix.LiveView
def render(assigns) do
Phoenix.View.render(AppWeb.PageView, "page.html", assigns)
end
end<h1>Temperature Control</h1>
<%= live_render(@conn, AppWeb.ThermostatLive) %>def weather_greeting(assigns) do
~H"""
<div title="My div" class={@class}>
<p>Hello <%= @name %></p>
<MyApp.Weather.city name="Kraków"/>
</div>
"""
endrender_to_string/4defmodule HelloWeb.ErrorHTMLTest do
use HelloWeb.ConnCase, async: true
import Phoenix.Template
test "renders 404.html" do
assert render_to_string(HelloWeb.ErrorHTML, "404", "html", []) == "Not Found"
end
test "renders 500.html" do
assert render_to_string(HelloWeb.ErrorHTML, "500", "html", []) == "Internal Server Error"
end
endimport Phoenix.LiveViewTest
test "renders greet component" do
assigns = %{messenger: "Phoenix"}
html = rendered_to_string(~H"""
<HelloWeb.HelloHTML.greet messenger={@messenger} />
""")
assert html =~ "Hello World, from Phoenix!"
endattr:for~praw/1attr