【Rails】繰り返し処理でメニューを簡単に作る方法


はじめに

下の記事「【爆速実装】夢と魔法のeach文【Rails-haml】」の記事を見て爆速でメニューが作れることを知りました。
https://qiita.com/enjoy_omame/items/c9f14f9cbb9bd2408a18
すげぇ。なんじゃこりゃ!

で、この記事なんですが、あくまで"簡単に作る方法"を解説してくれています。遷移先などの実装には触れていません。
きっと「ここからは自分で考えて実用的にしましょうね」という意図があるのだろう。と、言っているんだと思います。僕には聞こえました。そんな副音声が!
なので、自分なりに考えて実用的な実装をしてみたので今回記事にしてみます。

実装した内容

こんな感じのメニューを繰り返し処理で実装します。
今回は、usersコントローラ内のshowアクション(ここで繰り返し処理の元になるネタをしこむ)を介して、show.html.haml(ここで仕込んだネタを繰り返し処理して)表示するといった感じに処理が走ってます。

「【爆速実装】夢と魔法のeach文【Rails-haml】」の記事では、上記実装で言う、「プロフィール編集」「クレジットカード登録」「ログアウト」とかの文字列を繰り返し処理を使って実装する方法を紹介しています。

そのため、リンク先の設定とかについては触れていません。
なので、クリックした時の遷移先についても繰り返しで実装できるように挑戦しました!

実装結果

コントローラー

users_controller.rb(参考にした記事を自分なりに応用してみた)
  def show
    @user = User.find(params[:id])
    @contents = [{name:"プロフィール編集",path: "/profiles/edit",pattern: "GET"},{name: "クレジットカード登録", path:"#", pattern: "GET"},{name: "ログアウト", path: "/users/sign_out", pattern: "delete"}]
  end

※クレジットカードの導線はまだ決めてないので、遷移先を"#"で仮置きしてます。

ビュー

show.rb(部分テンプレートで切り出してます)
- @contents.each do |content|
  .MyPageSideMenuList
    .MyPageSideMenuList__Value
      = link_to content[:path],method: content[:pattern] do
        = content[:name]
    .MyPageSideMenuList__Disclosure
      = icon('fas','chevron-right')

解説(と、いうか実装に到るまでの脳内経緯)

「参考にした記事では、表示したい文字列をeach文を使って繰り返し処理で表示しているぞ。」

「ん?それなら、each文を使って繰り返し処理をする時にURLとかも一緒にインスタンス変数に入れられれば良い感じに遷移先も実装できるんじゃない?」

「そんなことできるのだろうか・・・。ん?まてよ、データベースから複数のレコードを取得している時って似たような事やってない?」

「あ、じゃあ配列の中身をハッシュにして、その中に文字列とURLをキーとして設定すればいいんじゃない?」

「ぉ?と言うことは、URLを設定するんだしHTTPメソッドも指定しないとルーティングエラー起こすぞこりゃ。methodを入れるキーも必要だ!」
(ごにょごにょ

えーと・・・端的に言いますと

配列の中に3つのキーを含んだハッシュを入れてみました。
ハッシュの内容はこんな感じ

キー(シンボル) 入れるバリュー
name: リストに表示したい文字列
path: 遷移させたいURL
pattern: HTTPメソッド
user_controller(代入内容の比較).rb
#before
@contents = ["プロフィール編集","クレジットカード登録","ログアウト"]
#After(ハッシュを配列に入れてる)
@contents = [{name: "プロフィール編集",path: "/profiles/edit",pattern: "GET"},{name: "クレジットカード登録", path: "#", pattern: "GET"},{name: "ログアウト", path: "/users/sign_out", pattern: "delete"}]

つまり、name: path: pattern:で1セットのハッシュにしてあげて、この塊をeach文で繰り返す。って感じです。

もうちょっと掘り下げて解説

show.html.haml
-#~省略(部分テンプレートで切り出してます)
- @contents.each do |content|
  .MyPageSideMenuList
    .MyPageSideMenuList__Value
      = link_to content[:path],method: content[:pattern] do
        = content[:name]
    .MyPageSideMenuList__Disclosure
      = icon('fas','chevron-right')

5行目の =link_to に注目です。

もし、繰り返しを使わずに、ログアウトの処理を記述する場合は、以下のような書き方ができます。
(※devise gem を利用している前提でのお話となります。)

sample.html.haml
-# URLをベタ書きした場合
= link_to "/users/sign_out", method: :delete do
 = "ログアウト"

今回は @contents をcontentに代入してeach文で繰り返し処理をしていますよね?
そのため、繰り返し処理の最中、contentさんは以下のように扱われます。

 #1週目のcontentの中身 → {name:"プロフィール編集",path: "#",pattern: "GET"}
 #2週目のcontentの中身 → {name: "クレジットカード登録", path:"#", pattern: "GET"}
 #3週目のcontentの中身 → {name: "ログアウト", path: "/users/sign_out", pattern: "delete"}

ではでは、この知識を前提として、繰り返し処理の3週目がどうなるかというと・・・。

show.html.haml
-#~省略(部分テンプレートで切り出してます)
- @contents.each do |content|
  .MyPageSideMenuList
    .MyPageSideMenuList__Value
      -#実際に記載されている下の2行が...
      = link_to content[:path],method: content[:pattern] do
        = content[:name]
      -#繰り返し処理の結果、下の2行の内容に書き換わる
      = link_to "/users/sign_out",method: :delete do
        = ログアウト
    .MyPageSideMenuList__Disclosure
      = icon('fas','chevron-right')

なんとなくイメージ掴めますかね?
最後は、上の結果を先ほどちょっと記述した
sample.html.haml(URLをベタ書きした時)と比較してみましょう!

sample.html.haml
-# URLをベタ書きした場合
= link_to "/users/sign_out", method: :delete do
 = "ログアウト"

どうでしょうか?同じ内容になってませんか?
これで、繰り返し処理で遷移先も実装が可能となりました!

まとめ

今回やったことをざっとまとめると・・・。

  • コントローラーの該当アクションで繰り返したいネタを配列で準備する
  • Hashの中身を{name: " ~~ ", path: " ~~ ", method: " ~~ "}を1セットとして配列に格納する
  • ヘルパーメソッド の=link_toの引数の箇所にhashのキーを置き換えてあげる

これだけを意識すれば、あら不思議!遷移できるメニューリストが作れちゃう!!
(補足)pathとmethodについては、ターミナルでrails routesをしてあげて、飛ばしたい遷移先がなんなのかを調べてあげれば問題なしです!

最後に

他にも実装方法を色々試しましたが、:pathにPrefixの値を記述した場合はどうもうまくいきませんでした。
理由としては、ダブルクォーテーションもしくはシングルクォーテーションで囲われた状態になってしまうからだと思います。

sample.html.haml
-#prefixの正しい指定方法
= link_to destroy_user_session_path, method: :delete do

-#@contentsの中に入れたハッシュの :path でprefixを指定した場合
= link_to "destroy_user_session_path", method: :delete do
-# →Prefixで指定しているのに""で囲われてしまうからprefixとして扱われない

解消方法やもっと良い実装方法などご存知の方いらっしゃいましたら、コメント頂けますと幸いです!

駄文にお付き合いくださりありがとうございました!