8
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Rails5】ドロップダウンリスト(ハンバーガーメニュー)の実装(コントローラー編)メルカリクローンサイトの作成

Last updated at Posted at 2019-10-09

#背景
カリキュラムの過程でご存知メルカリのクローンサイトを作りました。
その際に、ヘッダーメニューにあるドロップダウンリスト(ハンバーガーメニュー?)を実装したのでその記録です。

仕上がりはこんな感じです。
Image from Gyazo

css編を書きました。
こちらです。
【Rails5】ドロップダウンリストの実装(haml,scss編)〜メルカリクローンサイトの作成

#リストに呼び出したいDB構造など
今回、メルカリのサイトをクローンするにあたり、カテゴリーは、単一テーブル継承にすることで実装しています。

例えば、以下のような構造をしています。

id category parent_id
1 レディース
2 メンズ
13 トップス 1
14 ジャケット 2
115 Tシャツ 13
116 ダウンジャケット 14

この例だと、レディース>トップス>Tシャツ、 メンズ>ジャケット>ダウンジャケットという階層になります。
ちなみにビューで値を呼び出すときは、@category.childrenや@category.parentで子要素や親要素を呼び出すことが可能。

#ドロップダウンリストの要素が多すぎ
さて、実装に入りましょう。
色々な記事を参考にさせていただくと、ドロップダウンはCSSだけでいけることが判明。~~(後日別記事書きます)~~→こちら

じゃぁhtmlでカテゴリー名を全部入れればOKでしょ?

いや、待て待て。

カテゴリー名何個あんのよ
Image from Gyazo

親、子、孫合わせて
1,325!!

うん、多いね。

クラス名とかも一緒に書くから全部書くと4,000行の大作になっちゃうじゃない。

スマートじゃないね。
これ地道にやるならプログラミングやってる意味ないじゃん。

DBにデータはあるんだし、なんとかそこから引っ張ってこれないんか??

#リストの数が多いのでeachを使う
じゃぁ該当するカテゴリー名を変数に入れてrenderをeachさせればいいんじゃね?

まずは親カテゴリ。
親カテゴリはDBから呼び出さずにビューに記載。そして子カテゴリの呼び出し(@category_children1)

views/product/_header.html.haml

 %ul.header__menu-box--left__parents
    %li.header__menu-box--left__parents__parent
      = link_to "レディース", category_path(id: 1)
      %ul.header__menu-box--left__children
         = render partial: "header_child", collection: @category_children1, as: "category"

続いて子カテゴリ。そして孫カテゴリーの呼び出し(category.children)

views/product/_header_child.html.haml
%li.header__menu-box--left__children__child
  = link_to "#{category.category}", category_path(id: category.id)
  %ul.header__menu-box--left__grand-children
    = render partial: "header_grand-child", collection: category.children, as: "children"

さらに孫カテゴリ

views/product/_header_grand-child.html.haml
%li.header__menu-box--left__grand-children__grand-child
  = link_to "#{children.category}", category_path(id: children)

また、コントローラー側ではこのような処理がされています。

products_controller.rb
def set_categories
    @categories = Category.where(params[:id])
    @category_children1 = Category.where(parent_id: 1)
    @category_children2 = Category.where(parent_id: 2)
    @category_children3 = Category.where(parent_id: 3)
    @category_children4 = Category.where(parent_id: 4)
    @category_children5 = Category.where(parent_id: 5)
    @category_children6 = Category.where(parent_id: 6)
    @category_children7 = Category.where(parent_id: 7)
    @category_children8 = Category.where(parent_id: 8)
    @category_children9 = Category.where(parent_id: 9)
    @category_children10 = Category.where(parent_id: 10)
    @category_children11 = Category.where(parent_id: 11)
    @category_children12 = Category.where(parent_id: 12)
    @category_children13 = Category.where(parent_id: 13)
  end

よし、早速動かしてみよう!!!

おやおや、、、
確かに動くんだけど、リンクをクリックしたらエラーが。。。
読み込むテンプレートないじゃん!って怒られている
Image from Gyazo

部分テンプレートはちゃんとあるし、Missingな訳ないんだけどな

あれ、よーく見ると、ビューを読み込もうとしてるのってcategory_controllerじゃん
product_controllerじゃないのね。。。

そうか、部分テンプレートが同じcategoryフォルダにないから読めないのね。
次のように変更しましょ。

= render partial: "products/header_child"

こうすれば違うフォルダの部分テンプレートも読めるようになるね。

じゃ更新して、、、
Image from Gyazo
あれれ? あれれ?
今度は子と孫のカテゴリーが出なくなったぞ。。。

#ページごとで読み込むコントローラーが違う

ページごとに読み込むコントローラーが違うため、それぞれのコントローラーで変数の設定が必要になるのか。。。
全部のコントローラーに上のあれを書くのは流石に可読性が悪くなるし、
DRYの法則に逆らうね

だるいわ〜

なんとか共通化したりできないの??

#共通のメソッドConsernsにしてしまう
ありました、ちゃんとそういう機能。

参考記事
Ruby on RailsでConcernsを利用してControllerの処理を共通化する

早速、以下のように書きます。

controllers/concerns/common_actions.rb
module CommonActions
  
  extend ActiveSupport::Concern

  def set_categories
    @categories = Category.where(params[:id])
    @category_children1 = Category.where(parent_id: 1)
    @category_children2 = Category.where(parent_id: 2)
       #中略
    @category_children12 = Category.where(parent_id: 12)
    @category_children13 = Category.where(parent_id: 13)
  end

end
必要なコントローラー.rb
  include CommonActions
  before_action :set_categories, only: [必要なアクション]

  #最初に読み込む必要があるため、最初に追記します。

でもこれ、コントローラー全部に書き込むのもどうかと思うのでこうしてみました。

application_controller.rb
class ApplicationController < ActionController::Base
  include CommonActions
  before_action :set_categories
   #中略
end

こうすることで、全部のコントローラーに記述する必要がなくなりました!!

少しはいいの作れたかな〜
もっといい方法があればご教授くださいまし。

8
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?