Understanding templates

Templates are, at a very high level, a way to include common portions of content that should be included on multiple pages. By default, any call to render that renders an HTML template will have that render happen INSIDE of a template, and that template will be under /lib/vocial_web/templates/layout/app.html.eex. If we open up that file, we can see a base HTML structure and the default file that we’ll start off with:

<!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">
<meta name="description" content="">
<meta name="author" content="">

<title>Hello Vocial!</title>
<link rel="stylesheet" href="<%= static_path(@conn, "/css/app.css") %>">
</head>

<body>
<div class="container">
<header class="header">
<nav role="navigation">
<ul class="nav nav-pills pull-right">
<li><a href="http://www.phoenixframework.org/docs">Get Started</a></li>
</ul>
</nav>
<span class="logo"></span>
</header>

<p class="alert alert-info" role="alert"><%= get_flash(@conn, :info) %></p>
<p class="alert alert-danger" role="alert"><%= get_flash(@conn, :error) %></p>

<main role="main">
<%= render @view_module, @view_template, assigns %>
</main>

</div> <!-- /container -->
<script src="<%= static_path(@conn, "/js/app.js") %>"></script>
</body>
</html>

The one line you should especially pay attention to is the render call inside of the <main> tags. It's going to render the actual template that we specify inside of our controller (lib/vocial_web/templates/poll/index.html.eex). But, what if we want to use a special template?

Let's create a new template under lib/vocial_web/templates/layout called special.html.eex. Open the file up and give it the following contents:

<!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">
<meta name="description" content="">
<meta name="author" content="">

<title>Vocial - The Social Voting App!</title>
<link rel="stylesheet" href="<%= static_path(@conn, "/css/app.css") %>">
</head>

<body>
<div class="container">
<header class="header">
<h4>Logo</h4>
</header>

<p class="alert alert-info" role="alert"><%= get_flash(@conn, :info) %></p>
<p class="alert alert-danger" role="alert"><%= get_flash(@conn, :error) %></p>

<main role="main">
<%= render @view_module, @view_template, assigns %>
</main>

</div> <!-- /container -->
<script src="<%= static_path(@conn, "/js/app.js") %>"></script>
</body>
</html>

Next, we're going to hop back to our controller code inside poll_controller.ex. Change the index function to have the following body instead:

def index(conn, _params) do
conn
|> put_layout(:special)
|> render("index.html")
end
The |> operator you see in the preceding code is something called the "pipe" operator in Elixir. What this does is take the results of the previous expression and then pass that in as the first argument in the next function. That second line would, without the pipe operator, look like this: put_layout(conn, :special).

Here, we're telling Phoenix that we want this connection response to instead be wrapped in a different layout. There are two ways to pass this information to Phoenix: we can either use an atom, such as in the preceding example (where it will assume that what we actually meant was special.html) or we can use a string to represent it via put_layout(“special.html”). Reload your page and you should see everything wrapped inside of your new layout!

If everything looks good, we'll move on to passing along some data to our template!