Using Spacer Templates in Rails
I recently stumbled upon one of the more esoteric features of Rails when a client recently wanted to show the movement of an item through their physical locations. The request was pretty simple, we just wanted to show each “movement” in a card with a piece of UI between each set of movements.
I’ve done this in the past by looping over a collection and keeping track of the index (we don’t want an arrow at the end of our list pointing to an empty space).
<% @movements.each_with_index do |movement, index| %>
<%= render partial: "movement", movement: movement %>
<% unless index == (@movements.length - 1) %>
→
<% end %>
<% end %>
But we can do better! Rails is pretty smart about rendering collections using partials that are named conventionally.
# app/views/movements/index.html.erb
<div id="movements" class="min-w-full my-5">
<%= render partial: @movements, spacer_template: "movement_connection" %>
</div>
# app/views/movements/_movement.html.erb
<div id="<%= dom_id movement %>" class="bg-gray-100 p-4 flex justify-between">
<div class="space-x-2">
<span><%= movement.origin %></span>
<span>→</span>
<span><%= movement.destination %></span>
</div>
<date><%= time_ago_in_words(movement.date) %> ago</date>
</div>
# app/views/movements/_movement_connection.html.erb
<div class="space-y-1">
<svg viewBox="0 0 220 2" xmlns="http://www.w3.org/2000/svg">
<rect width="2" height="2" fill="#eee" />
</svg>
<svg viewBox="0 0 220 2" xmlns="http://www.w3.org/2000/svg">
<rect width="2" height="2" fill="#eee" />
</svg>
<svg viewBox="0 0 220 2" xmlns="http://www.w3.org/2000/svg">
<rect width="2" height="2" fill="#eee" />
</svg>
</div>
With the above, we can name our partials _movements.html.erb
and _movement_arrow.html.erb
and Rails will handle the index logic for us by only rendering our spacer template between items in the collection.
Voilà! We have a basic tracking UI. I did the SVG stuff on the fly so it currently breaks when you resize the page, but my focus was to display the helpful utility of spacer_template
.