はじめに
これまでは 投稿一覧(Item, ItemPost) をそれぞれ表示していましたが、
一覧ページ以外にもユーザーページや、今後作成予定のグループページなどでも「カード形式」で統一して表示したいと考えました。
そのため、カード表示を部分テンプレート化し、Item と ItemPost の両方で使えるように共通化しました。
これまでの記事はこちら👇
ポートフォリオ構築の振り返り(第1回:プロジェクト概要と設計)
ポートフォリオ構築の振り返り(第2回:Railsアプリ立ち上げ〜トップページ表示)
ポートフォリオ構築の振り返り(第3回:Deviseでログイン機能を実装)
ポートフォリオ構築の振り返り(第4回:ヘッダーの作成とログイン機能の実装)
ポートフォリオ構築の振り返り(第5回:投稿機能と画像投稿フォームの作成)
ポートフォリオ構築の振り返り(第6回:投稿機能の作成)
ポートフォリオ構築の振り返り(第7回:ユーザーカラム追加)
ポートフォリオ構築の振り返り(第8回:部分テンプレートを使ったマイページ作成)
手順の流れ
- 部分テンプレート
_card.html.erbを作成 -
is_a?を利用して Item と ItemPost を判別 - 共通レイアウトにまとめ、再利用可能に
1. 部分テンプレート _card.html.erb の作成
app/views/shared/_card.html.erb
<div class="col-12 col-sm-6 col-md-4 col-lg-3 mb-4">
<div class="card h-100 position-relative rounded-4">
<!-- 画像とリンク -->
<%= link_to item_path(card.is_a?(Item) ? card.id : card.item.id) do %>
<%= image_tag(card.is_a?(Item) ? card.image : card.item.image, class: "card-img-top object-fit-cover") %>
<% end %>
<!-- ステータス -->
<div class="position-absolute top-0 start-0 m-2 px-2 py-1 btn btn-sm no-hover <%= status_color_class(card.status) %>">
<%= card.status_i18n %>
</div>
<!-- 非公開マーク -->
<% if card.is_a?(Item) ? card.private : card.item.private %>
<div class="position-absolute top-0 end-0 m-2 px-2 py-1 bg-white rounded small d-flex align-items-center gap-1 shadow-sm"
style="border: 1px solid #ffc107; color: #e69500;">
<i class="fa-solid fa-eye-slash"></i>
</div>
<% end %>
<div class="card-body d-flex flex-column">
<!-- タイトル・本文 -->
<div class="mb-auto">
<%= link_to item_path(card.is_a?(Item) ? card.id : card.item.id) do %>
<h5 class="card-title">
<%= card.is_a?(Item) ? card.title : card.item.title %>
</h5>
<p class="card-text">
<%= truncate(card.is_a?(Item) ? card.body : card.review, length: 40) %>
</p>
<% end %>
</div>
<!-- カテゴリ -->
<div class="btn btn-sm no-hover <%= category_color_class(card.is_a?(Item) ? card.category : card.item.category) %>">
<%= card.is_a?(Item) ? card.category_i18n : card.item.category_i18n %>
</div>
<!-- 期限(自分の投稿のみ) -->
<div>
<% deadline = card.is_a?(Item) ? card.deadline : card.item.deadline %>
<% user = card.user %>
<% if user == current_user && deadline.present? %>
<div class="text-muted small mt-1">
期限:<%= deadline.strftime('%Y/%m/%d') %>
</div>
<% end %>
</div>
<!-- ユーザー名 or グループ名 -->
<div class="d-flex align-items-center mt-2">
<% group = card.is_a?(Item) ? card.group : card.item.group %>
<% user = card.user %>
<% if group.present? %>
<%= link_to group_path(group), class: "d-flex align-items-center text-decoration-none" do %>
<%= image_tag group.image, size:"50x50", class: "rounded-3 me-2" %>
<div>
<strong><%= group.name %></strong><br>
<small class="text-muted">
(by <%= (user.withdrawn? || user.deactivated?) ? '退会したユーザー' : user.name %>)
</small>
</div>
<% end %>
<% else %>
<%= link_to user_path(user.id), class: "d-flex align-items-center text-decoration-none" do %>
<%= image_tag user.get_profile_image(50, 50),
style: "width: 50px; height: 50px; object-fit: cover;",
class: "rounded-circle me-2" %>
<p class="mb-0">
<%= (user.withdrawn? || user.deactivated?) ? '退会したユーザー' : user.name %>
</p>
<% end %>
<% end %>
</div>
</div>
</div>
</div>
-
Item と ItemPost の両方を受け取れるよう条件分岐
card.is_a?(Item) ? ... : ... のように分岐し、
親(Item)のデータか子(ItemPost)のデータかを判定して表示。(次で作り方まとめます) -
ステータスやカテゴリを色付きラベルで表示
状態をひと目でわかるように。 -
「期限」は自分の投稿だけ表示
他人には見せず、自分だけが管理できるように制御。 -
ユーザー or グループ表示
投稿がグループに属していればグループを表示、(何回か後に作り方まとめます)
そうでなければユーザーを表示。
退会済ユーザーは「退会したユーザー」として表示。
2. コードの説明
-
card.is_a?(Item)
そのオブジェクトが Item か ItemPost かを判定 -
status_i18n,category_i18n
enum を日本語化して表示 - 非公開アイコン(
fa-eye-slash)
private が true の場合に表示 - ユーザー表示
退会済み or 無効化ユーザーの場合は「退会したユーザー」として表示
3. 利用方法
一覧ページ・ユーザーページ・グループページなどで以下のように呼び出せます。
<%= render partial: "shared/card", collection: @cards, as: :card %>
これにより、どのページでも同じデザインでカードを使い回せるようになりました。
まとめ
今回は Item と ItemPost をまとめてカード表示する部分テンプレート を作成しました。
今後ユーザーページやグループページでも統一的に使えるため、再利用性・保守性がアップしました!
次は、投稿に付属した 経過の投稿 ができるようにします!
ポートフォリオ構築の振り返り(第10回:親子関係/リレーションを作る)
用語説明
-
部分テンプレート(partial)
ビューを部品化して共通化する仕組み。_card.html.erbのように_で始める。 -
is_a?
Ruby のメソッドで、オブジェクトが指定したクラスのインスタンスかどうかを判定。 -
truncate
長い文字列を省略して表示するための Rails ヘルパー。 -
enum + i18n
モデルで定義したステータスやカテゴリを、Rails の国際化(i18n)で日本語に変換して表示できる。 -
collection: @cards, as: :card
複数のオブジェクトをまとめて部分テンプレートに渡す書き方。それぞれの要素がcardとして使える。
---