Subscribe to access all episodes. View plans →
Published May 27, 2019
Elixir 1.8
Phoenix 1.4
Here we have an application that lists some albums. It also has a blog that we can see is using the same layout as the other pages. In this episode let’s see how we can render different layouts for different pages of our application, specifically our blog.
The first thing we’ll want to do is create a new file in our templates/layout
directory named blog.html.eex
. I’ll paste in the layout that we want to use for our blog.
Template path: lib/teacher_web/templates/layout/blog.html.eex
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Blog</title>
<link rel="stylesheet" href="<%= Routes.static_path(@conn, "/css/app.css") %>"/>
</head>
<body class="blog">
<main role="main" class="container">
<%= link "Albums", to: Routes.album_path(@conn, :index) %>
| <%= link "Blog", to: Routes.post_path(@conn, :index) %>
<p class="alert alert-info" role="alert"><%= get_flash(@conn, :info) %></p>
<p class="alert alert-danger" role="alert"><%= get_flash(@conn, :error) %></p>
<%= render @view_module, @view_template, assigns %>
</main>
<script type="text/javascript" src="<%= Routes.static_path(@conn, "/js/app.js") %>"></script>
</body>
</html>
Now that we have our new layout, we need to tell our application to use it when we render our blog page.
Let’s open our post_controller.ex
and one way we can specify the layout to use is in the render
function. We see we can use the layout option with two element tuple that has our layout view module and our template.
So let’s go to our index
action and we can pass in our layout.
lib/teacher_web/controllers/post_controller.ex
...
def index(conn, _params) do
posts = Blog.list_posts()
render(conn, "index.html",
posts: posts,
layout: {TeacherWeb.LayoutView, "blog.html"})
end
...
Now let’s go back to our browser and if we go to our “blog” page. Great - we see our blog specific layout with a new background color and font style for our blog’s title. Since we are only rendering our blog layout for the “index” action, if we click into a specific post, we see our blog template is no longer rendered.
We’ll go back to our controller and update it so that all of our actions rendered for our blog controller use the blog template. To do that, we can use the Phoenix.Controller.put_layout
function.
Let’s go ahead and remove the template from our render
function. Then we’ll add plug :put_layout
with the name of our layout template - “blog.html”.
lib/teacher_web/controllers/post_controller.ex
...
plug :put_layout, "blog.html"
...
Then if we go back to the blog post and refresh the page we see our new layout is being used. Now this works great for layouts that are specific to a single controller. But if we needed to use this same layout template for multiple controllers, we would have to add it to each controller. Another approach would be to create a new pipeline
in our router and then use it for all the controllers we want to use that layout for. Let’s try this.
We’ll go back to our post_controller.ex
and remove our put_layout
and then let’s open our router and create a new pipeline
that we’ll call blog. Then inside of the pipeline we’ll add plug :put_layout
and here we’ll need to include our view as the first element in a two element tuple with our template.
Let’s create a new scope for our controllers that we want to use the blog layout. And we’ll include pipe_through
with the browser and blog and let’s include our posts resource and our albums.
lib/teacher_web/router.ex
...
pipeline :blog do
plug :put_layout, {TeacherWeb.LayoutView, "blog.html"}
end
scope "/", TeacherWeb do
pipe_through [:browser]
resources "/sessions", SessionController, only: [:new, :create]
delete "/sign-out", SessionController, :delete
resources "/registrations", RegistrationController, only: [:new, :create]
# resources "/albums", AlbumController
# resources "/posts", PostController
get "/", PageController, :index
end
scope "/", TeacherWeb do
pipe_through [:browser, :blog]
resources "/albums", AlbumController
resources "/posts", PostController
end
...
Then if we go back to the browser we can see that both our albums and our blogs are using our new layout.
ehayun
5 years agoGreat but I wonder how to use a different layout in liveview? I have this code:
Where to add the new layout?
Thanks
Alekx
5 years agoYou could try setting it in the controller if you want all of the actions to use the same template:
If you only want your
index
action that’s rendering theLiveView
to have the layout, this should work:Hope this helps!
ehayun
5 years agoworks like a magic. Thanks
Ivan Eliseo Tinajero Diaz
4 years agoHow can I get a completely layout, I mean an alternative layout even with its own HTML BODY tags!
Alekx
4 years agoDifferent from what’s in this episode?
In the episode we’re rendering a completely different layout for a blog, complete with a different
title
tag andbody
from what the other pages of the app use.