目標
下記のようなドロップダウンメニューを作成しましたので記載します。
今回はjQueryではなくVue.jsを使って作成してみました。
作成の仕方はVue.jsでなく、普通にjsを使っても全く問題ありませんが、
方法の一つとして、参考になればと思います。
記述量自体はjs書くより減った様な気がします
・カテゴリ一覧をドロップダウンで表示させる
・カテゴリーの値はデータベースから取得する
・mouseoverでカテゴリの子要素を表示、mouseleaveで非表示となる
・クリックで各カテゴリページに遷移する
・Rails+Vue.jsで作成する
不備等多々あるかと思いますが、参照記事は良い記事ばかりです。
間違いやより良い記述方法があればご教示ください

①Vue.js導入
Webpackerのインストール
gem 'webpacker', github: 'rails/webpacker'
bundle install
yarnのインストール
brew install yarn
Webpackerインストール
rails webpacker:install
create config/webpacker.yml
Copying webpack core config
create config/webpack
create config/webpack/development.js
create config/webpack/environment.js
create config/webpack/production.js
create config/webpack/test.js
Copying postcss.config.js to app root directory
create postcss.config.js
Copying babel.config.js to app root directory
create babel.config.js
Copying .browserslistrc to app root directory
create .browserslistrc
Creating JavaScript app source directory
create app/javascript
create app/javascript/packs/application.js
apply /Users/sotatakahashi/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/bundler/gems/webpacker-2387331b33c6/lib/install/binstubs.rb
Copying binstubs
exist bin
create bin/webpack
create bin/webpack-dev-server
append .gitignore
Installing all JavaScript dependencies [4.0.2]
run yarn add @rails/webpacker from "."
:
✨ Done in 7.46s.
Webpacker successfully installed 🎉 🍰
↑終わったら絵文字が出ます(かわいい)
Vueインストール
rails webpacker:install:vue
これでRails上でVueを使う準備はOKです!
詳しくは下記記事にて。
参考記事
https://qiita.com/cohki0305/items/582c0f5ed0750e60c951
https://qiita.com/saongtx7/items/fdb77901e7fcf291e2ad
②カテゴリーメニューの作成
マウスオーバーとかは後にして、とりあえず見た目の部分を作っていきます。
(CSS割愛します)
最初に箱をカテゴリの数だけ用意します。
.category_box
.genre-box
%ul.genre
- @parents.each do |parent|
= link_to category_path(parent.id), method: :get do
%li.genre_list
=parent.name
.next-box
.sub-box
%ul.sub
%li.sub_list
.detail-box
%a
%ul.detail
%li.detail_list
親、子、孫にそれぞれulとliを持たせて、リストを作成します。
基本はこの箱を使ってドロップダウンメニューを作成していきます!
③子要素リストの切り替えと追加、マウスオーバーで表示/非表示を切り替える
Vue.jsのメソッドのv-on, v-show, v-for, v-bindを使用します。
v-show:
イベント発火のタイミングを決めて、表示と非表示を切り替える。
(mouseover,mouseleaveで表示非表示を切り替えています)
v-on:
イベント発火のタイミングを決めて、特定のメソッドを動かす。
(mouseoverでカテゴリ情報を取得し、子カテゴリーがあればAPIで用意します)
v-for:
繰り返し処理を行う
(選択したカテゴリーの小カテゴリーがあれば次の箱で繰り返しliを作成しています)
v-bind:
データのバインディングを行う
(v-forで繰り返し作成したliに対してリンクを張っています)
.category_box{"v-show": "menuBool","v-on:mouseleave": "hiddenMenu"}
#v-showなどはvueで使われるアクション、menuの表示非表示をこちらで設定
.genre-box
%ul.genre
- @parents.each do |parent|
= link_to category_path(parent.id), method: :get do
%li.genre_list{'v-on:mouseover': "setCategoryInfo(#{parent.id})"}
=parent.name
.next-box{'v-show': 'categoryInfoBool',"v-on:mouseleave": "hiddenSubInfo"}
#子カテゴリーのメニューの表示、非表示をこちらで設定
.sub-box
%a
%ul.sub
%li.sub_list{"v-on:mouseover": "setSubInfo(category.id)"}
{{category.name}}
#マウスオーバーした時に値を取得、表示させる。{{}}には表示する内容が入り、vueで切り替えられる!
.detail-box{'v-show': 'SubInfoBool'}
%a{"v-for": "sub in SubInfo","v-bind:href": "'/categories/' + sub.id"}
%ul.detail
%li.detail_list
{{sub.name}}
new Vue({
el:"#app",
data:{
categoryInfo:[],
//子カテゴリーの情報を入れる箱
SubInfo:[],
//孫カテゴリーの情報を入れる箱
menuBool: false,
categoryInfoBool: false,
SubInfoBool: false,
//基本非表示にさせるため、falseとしておく
},
methods: {
setCategoryInfo(id){
axios.get(`/api/genres/${id}.json`)
//axiosでapiを使用。apiでは取得したカテゴリの子要素を用意する
.then(res => {
this.categoryInfo = res.data;
this.categoryInfoBool = true;
});
},
setSubInfo(id){
axios.get(`/api/genres/${id}.json`)
.then(res => {
this.SubInfo = res.data;
this.SubInfoBool = true;
});
},
showMenu:function(){
return this.menuBool = true
},
hiddenMenu:function(){
return this.menuBool = false;
},
hiddenCategory:function(){
return this.categoryInfoBool = false;
},
hiddenSubInfo:function(){
return this.SubInfoBool = false
},
//viewに乗っかった時にtrue,falseを切り替える
}
});
api/genre
show.json.jbuilder
json.array! @category_children do |category_child|
json.name category_child.name
json.id category_child.id
end
④それぞれにリンクを貼る
%header#app.header
.header__top
.header__top__logo
= link_to root_path do
%img{alt: "mercari", src: "//www-mercari-jp.akamaized.net/assets/img/common/common/logo.svg?228111635"}/
.header__top__search
= form_tag(search_items_path, method: :get, class: "header__top__search__box") do
%input.header__top__search__box__input{name: "keyword", placeholder: "何かお探しですか?", type: "search"}/
%button.header__top__search__box__submit{type: "submit"}= fa_icon ("search lg")
%form{"accept-charset" => "utf-8", :action => "/", :method => "get"}
.header__bottom
.header__bottom__leftside{"v-on:mouseleave": "hiddenMenu"}
.header__bottom__leftside__left_box{"v-on:mouseover": "showMenu","v-on:mouseleave": "hiddenCategory"}
= link_to categories_path, class: "header__bottom__leftside__left_box__content" do
= fa_icon ("list-ul")
%span カテゴリから探す
-# ここからドロップダウンメニュー
.category_box{"v-show": "menuBool","v-on:mouseleave": "hiddenMenu"}
.genre-box
%ul.genre
- @parents.each do |parent|
= link_to category_path(parent.id), method: :get do
%li.genre_list{'v-on:mouseover': "setCategoryInfo(#{parent.id})"}
=parent.name
.next-box{'v-show': 'categoryInfoBool',"v-on:mouseleave": "hiddenSubInfo"}
.sub-box
%a{"v-for": "category in categoryInfo","v-bind:href": "'/categories/' + category.id"}
#v-bindでaタグにリンクを貼っつける!
%ul.sub
%li.sub_list{"v-on:mouseover": "setSubInfo(category.id)"}
{{category.name}}
.detail-box{'v-show': 'SubInfoBool'}
%a{"v-for": "sub in SubInfo","v-bind:href": "'/categories/' + sub.id"}
%ul.detail
%li.detail_list
{{sub.name}}
-# ここまでドロップダウンメニュー
.header__bottom__leftside__right_box
= link_to '', class: "header__bottom_box__leftside__right_box__content" do
= fa_icon ("tag")
%span ブランドから探す
.header__bottom__rightside
.header__bottom__rightside__bell
= link_to '', class: "header__bottom__rightside__bell__content" do
= fa_icon("bell lg")
%span お知らせ
.header__bottom__rightside__check
= link_to '', class: "header__bottom__rightside__check__content" do
%span
= fa_icon("check lg")
%span やることリスト
.header__bottom__rightside__mypage
= link_to '', class: "header__bottom__rightside__mypage__content" do
%img.header__bottom__rightside__mypage__content__icon{alt: "", src: "//static.mercdn.net/images/member_photo_noimage_thumb.png", width: "32"}/
%div マイページ
new Vue({
el:"#app",
data:{
categoryInfo:[],
SubInfo:[],
menuBool: false,
categoryInfoBool: false,
SubInfoBool: false,
},
methods: {
setCategoryInfo(id){
axios.get(`/api/genres/${id}.json`)
.then(res => {
this.categoryInfo = res.data;
this.categoryInfoBool = true;
});
},
setSubInfo(id){
axios.get(`/api/genres/${id}.json`)
.then(res => {
this.SubInfo = res.data;
this.SubInfoBool = true;
});
},
showMenu:function(){
return this.menuBool = true
},
hiddenMenu:function(){
return this.menuBool = false;
},
hiddenCategory:function(){
return this.categoryInfoBool = false;
},
hiddenSubInfo:function(){
return this.SubInfoBool = false
},
}
});
かんせい!
Vue楽しいので何か違うこともやってみたいです!コンポーネントをそれぞれ作って表示とかも楽しそう
参考記事
インストール〜作成までの流れ
https://qiita.com/cohki0305/items/a678b0b17c5b496c1de9
https://qiita.com/saongtx7/items/fdb77901e7fcf291e2ad
axiosの使い方
https://www.webprofessional.jp/fetching-data-third-party-api-vue-axios/
https://qiita.com/ryouzi/items/06cb0d4aa7b6527b3645
vue.jsでmouseoverした時だけ表示する方法
https://qiita.com/sukechansan/items/07a415e4e7e5ce358afc
vue.jsでのリンクの貼り方
https://qiita.com/asaokamei/items/6afa7e2f33207d041588