基本

Ruby on Railsのビューで繰り返し処理(each)を使う

Ruby on Railsのビューでは、Rubyの繰り返し処理をそのまま使用して、配列やコレクションのデータを一覧表示できます。データベースから取得した複数のレコードを表示する場合や、メニュー項目をリスト化する場合など、Webアプリケーション開発において繰り返し処理は頻繁に使われます。この記事では、ビュー内での繰り返し処理の書き方と実践的な活用方法を解説します。

基本的な使い方

最も基本的な繰り返し処理は、eachメソッドを使ったものです。配列やコレクションの各要素に対して同じHTMLを繰り返し生成します。

app/views/posts/index.html.erb
<% numbers = [1, 2, 3, 4, 5] %>

<% numbers.each do |number| %>
  <div><%= number %></div>
<% end %>

上記のコードは、配列の各要素を<div>タグで囲んで出力します。eachの後のdo |number|部分で、各要素をnumberというブロック変数に代入しています。<% %>タグで制御構文を記述し、<%= %>タグで値を出力するのがポイントです。

コントローラーからのデータ表示

実際の開発では、コントローラーで取得したデータをビューで繰り返し表示するのが一般的です。

app/controllers/posts_controller.rb
class PostsController < ApplicationController
  def index
    @posts = Post.all
  end
end
app/views/posts/index.html.erb
<h1>投稿一覧</h1>

<% @posts.each do |post| %>
  <div class="post-card">
    <h2><%= post.title %></h2>
    <p><%= truncate(post.content, length: 100) %></p>
    <small><%= post.created_at.strftime("%Y/%m/%d") %></small>
    <%= link_to "詳細を見る", post_path(post) %>
  </div>
<% end %>

@postsはActiveRecordのコレクションで、eachで各投稿を1つずつ取り出してHTMLカードとして表示しています。truncateヘルパーで本文を100文字に切り詰め、link_toで詳細ページへのリンクを生成しています。

each_with_indexで連番を付ける

各要素にインデックス番号(連番)を付けたい場合は、each_with_indexを使います。

app/views/posts/index.html.erb
<table>
  <thead>
    <tr>
      <th>No.</th>
      <th>タイトル</th>
      <th>投稿日</th>
    </tr>
  </thead>
  <tbody>
    <% @posts.each_with_index do |post, index| %>
      <tr>
        <td><%= index + 1 %></td>
        <td><%= post.title %></td>
        <td><%= post.created_at.strftime("%Y/%m/%d") %></td>
      </tr>
    <% end %>
  </tbody>
</table>

each_with_indexのブロック変数は2つで、第1引数が要素、第2引数がインデックス(0始まり)です。表示上は1始まりにしたい場合はindex + 1とします。

データが空の場合の処理

繰り返し処理を行う際は、データが空の場合の処理も合わせて記述するのがベストプラクティスです。

app/views/posts/index.html.erb
<% if @posts.any? %>
  <% @posts.each do |post| %>
    <div class="post-card">
      <h2><%= post.title %></h2>
      <p><%= post.content %></p>
    </div>
  <% end %>
<% else %>
  <div class="empty-state">
    <p>まだ投稿がありません。</p>
    <%= link_to "最初の投稿を作成", new_post_path %>
  </div>
<% end %>

.any?メソッドでデータが1件以上あるか確認し、空の場合は代替メッセージを表示します。ユーザー体験の観点からも、空の状態を適切にハンドリングすることは重要です。

ネストした繰り返し処理

カテゴリごとに商品を表示するなど、繰り返しの中にさらに繰り返しを入れることもできます。

app/views/categories/index.html.erb
<% @categories.each do |category| %>
  <h2><%= category.name %></h2>
  <ul>
    <% category.products.each do |product| %>
      <li><%= product.name %> - <%= product.price %>円</li>
    <% end %>
  </ul>
<% end %>
注意

ネストが深くなるとビューが複雑になり、N+1クエリ問題(データベースへの問い合わせが大量に発生する問題)が起きやすくなります。コントローラーでincludesを使って関連データを事前に読み込んでおきましょう。

app/controllers/categories_controller.rb
# N+1問題を防ぐ
@categories = Category.includes(:products).all
ポイント

繰り返しで生成するHTML部品が複雑な場合は、パーシャルに切り出してコードを整理しましょう。<%= render partial: "post", collection: @posts %>のようにコレクションレンダリングを使うと、さらにすっきりと書けます。

まとめ

  • <% @items.each do |item| %> ... <% end %>で繰り返し処理を記述する
  • each_with_indexで各要素にインデックス番号を付けられる
  • データが空の場合は.any?で確認し、代替表示を用意する
  • ネストした繰り返しではincludesを使ってN+1問題を防ぐ
  • 複雑なHTML部品はパーシャルに切り出してコードを整理する
  • コレクションレンダリングで繰り返しパーシャルを効率的に表示できる