Check out our new series - Moving to Elixir

×

#23: Partial Templates with Phoenix

Elixir 1.4.5

Phoenix 1.3

Source code on GitHub


Phoenix makes rendering partial templates - or partials - dead-simple.

In this episode we’ll extract two different pieces of our template into partial templates that different templates can easily render.

Let’s get started.

Here we’re listing all movies from our database.

If we look at the movie index template we’re looping through all our movies and rendering rendering data about each one.

Let’s take this part of our template and extract it into a partial.

We’ll create a _movie.html.eex template in our same directory.

And in it we’ll copy over the part of template we want.

Now one change we’ll need to make is to our movie variable. Since this is a new template we’ll need to access our movie with @movie - the @ is macro that will essentially convert @movie to Map.get(assigns, :movie).

Template path: lib/teacher_web/templates/movie/_movie.html.eex

<tr>
  <td><%= @movie.title %></td>
  <td><%= @movie.summary %></td>
  <td><%= @movie.year %></td>

  <td class="text-right">
    <%= link "Show", to: movie_path(@conn, :show, @movie), class: "btn btn-default btn-xs" %>
    <%= link "Edit", to: movie_path(@conn, :edit, @movie), class: "btn btn-default btn-xs" %>
    <%= link "Delete", to: movie_path(@conn, :delete, @movie), method: :delete, data: [confirm: "Are you sure?"], class: "btn btn-danger btn-xs" %>
  </td>
</tr>

Now we can go back to our index.html template and update our list comprehension to render our new template.

We’ll call render, with the template we want to render, in this case our _movie.html, and then our assigns, which will be our ‘movie’.

We’ll also include our connection since we’re accessing it in the template.

Template path: lib/teacher_web/templates/movie/index.html.eex

  <%= for movie <- @movies do %>
    <%= render "_movie.html", movie: movie, conn: @conn %>
  <% end %>

Now let’s go to the browser and our movies are still be loaded correctly.

While this works, because we’re rendering a collection, let’s update it to use the render_many function.

We’ll go back to our template and call render_many which takes our collection, in this case our movies, our view of MovieView, the template we want to render, and our assigns. In this case we only need to include our connection.

Then we can remove our list comprehension.

Template path: lib/teacher_web/templates/movie/index.html.eex

<%= render_many @movies, TeacherWeb.MovieView, "_movie.html", conn: @conn %>

Then let’s go back to the browser and great - every thing looks good.

Now if we look at the top of our page, we see the title of our app and some navigation links.

These should be displayed on all pages, but if we click on our ‘About’ link - we see they’re missing. Let’s fix that.

We’ll again open our index.html.eex and at the top of the file is the piece we want to extract.

Now we could add a partial to our ‘layout’ directory and render it similar to how we did with our ‘movie’ partial, but let’s try a different approach and create it as a ‘shared’ template that many templates can access.

We’ll create a new directory in templates called ‘shared’ and inside it we’ll create our template _header.html.eex and we’ll paste in our content.

Template path: lib/teacher_web/templates/shared/_header.html.eex*

<div class="nav-header">
  <h1>Movie App</h1>
  <div class="nav-link">
    <%= link "All Movies", to: movie_path(@conn, :index) %>
    <%= link "About", to: page_path(@conn, :about) %>
  </div>
</div>

Now we’ll need to create a view. We’ll create a new file in ‘views’ called shared_view.ex. Then let’s define our new view module.

lib/teacher_web/views/shared_view.ex

defmodule TeacherWeb.SharedView do
  use Teacher.Web, :view
  
end

With that let’s go to where we want to render this template - in this case our app.html.eex

And we’ll render our template here. Even though our template is in a different directory, we can still render it.

We’ll just call render, with the view module, the template we want to use, and the assigns.

Template path: lib/teacher_web/templates/layout/app.html.eex

<main role="main">
  <%= render TeacherWeb.SharedView, "_header.html", assigns %> 
  <%= render @view_module, @view_template, assigns %>
</main>

Let’s go back to our browser and if we click through to different pages - we see our header content is being displayed on every page.