Tip: Use Rails `cycle` to avoid `i % 2 == 0` in your view loops
Sometimes you need to keep track of how many times you’ve looped when rendering some views, for instance to alternate background colors to create a “striped” table.
<!-- @foods = ["apple", "orange", "banana"] -->
<% @foods.each do |food| %>
<tr class="???">
<td><%= food %></td>
</tr>
<% end %>
You might try using :odd
or :even
CSS child selectors or switching to each_with_index
.
<% @foods.each_with_index do |food, i| %>
<tr class="<%= i % 2 == 0 ? 'bg-gray-200' : 'bg-gray-100' %>">
<td><%= food %></td>
</tr>
<% end %>
You could even refactor a bit to use i.odd?
or i.even?
.
<% @foods.each_with_index do |food, i| %>
<tr class="<%= i.even? ? 'bg-gray-200' : 'bg-gray-100' %>">
<td><%= food %></td>
</tr>
<% end %>
Rails offers a different helper that comes in handy for these situations: cycle
.
Usage
The cycle
helper takes an array of arguments and loops through them each time it is called.
We could replace the above code with:
<% @foods.each_with_index do |food, i| %>
<tr class="<%= cycle('bg-gray-200', 'bg-gray-100') %>">
<td><%= food %></td>
</tr>
<% end %>
The real benefits start to appear if you need more than two options.
<% @foods.each_with_index do |food, i| %>
<tr class="<%= cycle('bg-red-100', 'bg-orange-100', 'bg-yellow-100') %>">
<td><%= food %></td>
</tr>
<% end %>
If you need to manually reset or share a cycle
between code, you can pass a name:
key as an option.
cycle("red", "white", "blue", name: "colors")
cycle("sm", "md", "lg", "xl", name: "sizes")
reset_cycle("colors")
reset_cycle("sizes")
You can also use any object that responds to to_s
in the cycle.
<% @items.each do |item| %>
<div class="rotate-<%= cycle(0, 45, 90, 135, 180) %>">
<%= item %>
</div>
<% end %>
Additional Resources
Rails API Docs: TextHelper#cycle
Tailwind Docs: Even/odd variants