8

I have a loop for my Activity model

<% @activities.each do |activity| %>
<div>
    <div class="cell">
        <%= image_tag(activity.image) %>
    </div>
    <div class="cell">
        <h4><%= activity.title %></h4>
        <p><%= activity.description %></p>
        <span><%= activity.distance %></span>
    </div>
</div>
<% end %>  

I want to create a zig-zag effect, so I need to re-arrange the HTML for every even activity, so the markup will look like this:

<div>
  <div class="cell">
    <h4><%= activity.title %></h4>
    <p><%= activity.description %></p>
    <span><%= activity.distance %></span>
  </div>
  <div class="cell">
    <%= image_tag(activity.image) %>
  </div>
</div>

Is there a way to do this with an if statement? Something like this:

<% if activity.odd? %>
 <% @activities.each do |activity| %>
   <div>
     <div class="cell">
   <%= image_tag(activity.image) %>
 </div>
 <div class="cell">
   <h4><%= activity.title %></h4>
   <p><%= activity.description %></p>
   <span><%= activity.distance %></span>
 </div>
   </div>
  <% end %>
<% else %>
  <% @activities.each do |activity| %>
    <div>
      <div class="cell">
    <h4><%= activity.title %></h4>
    <p><%= activity.description %></p>
    <span><%= activity.distance %></span>
  </div>
  <div class="cell">
    <%= image_tag(activity.image) %>
      </div>
</div>
<% end %>
colmtuite
  • 4,311
  • 11
  • 45
  • 67

1 Answers1

17

There are a few ways to do this.

  1. The first and best way is to do this with CSS's nth-child selector, assuming it's possible to achieve your desired effect purely through CSS. Use nth-child(odd) or nth-child(even), as described in Alternate table row color using CSS?

  2. If you're iterating over a collection using .each, you can add a .with_index and check whether the index is odd or even:

    <% @activities.each.with_index do |activity, index| %>
      <% if index.odd? %>
        ...
      <% else %>
        ...
      <% end %>
    <% end %>
    

    Note that by default the index is zero-based, so your first element will be considered "even". You can pass a starting index of 1 to with_index if you want the alternating to start odd instead:

    <% @activities.each.with_index(1) do |activity, index| %>
    
  3. An alternative to using with_index is to use Rails' cycle helper, which "magically" returns the next argument in rotation each time its invoked:

    <% @activities.each do |activity| %>
      <% if cycle('odd', 'even') == 'odd' %>
        ...
      <% end %>
    <% end %>
    

Also note that this is a fantastic opportunity for refactoring. You should distill the two reused cell divs into their own partials and render them in the order you want:

<% if index.odd? %>
  <%= render h4_part %>
  <%= render img_part %>
<% else %>
  <% render img_part %>
  <% render h4_part %>
<% end %>
user229044
  • 232,980
  • 40
  • 330
  • 338
  • or you could use the same html for odd and even divs, but solve the zig-zag in the css, also using the cycle-trick to set the classes of the divs. – niels Aug 25 '12 at 14:56
  • @niels That would be my preferred solution – user229044 Aug 25 '12 at 15:19
  • @niels I've tried for an hour to solve the problem with CSS. I was using nth-child, but that's not compatible with IE. I tried flipping the divs using table-header-row, but then I can't float them left. – colmtuite Aug 25 '12 at 17:33
  • If you want to use css then use meagar's `cycle` method to add class="odd" or class="even" to each row, also change the class names from just "cell" to "image-cell" and "info-cell" (or something). Then in your css you can do something like `.odd .image-cell {float: left;} .odd .info-cell {float: right;}` and `.even .image-cell {float: right;} .even .info-cell {float: left;}` – Peter Duijnstee Aug 25 '12 at 17:45