どうも、ヒロキです。
今回はRails7でメニューバーのタブの切り替えをJavaScript、およびそのフレームワークのStimulusを使って実装してみました。
今後、同じ機能を再現するための覚書用と、後学のため記事にまとめておきます。
どなたかの参考になれば幸いです。
開発環境
- Ruby 3.1.2
- Ruby on Rails 7.0.4
- tailwindcss 3.1.8
- M1 Macbook Air 2020
- mac OS Monterey (ver. 12.4)
- ターミナル bash (Rosetta 2 使用)
出来上がりイメージ
以下のようなイメージで、メニュータブの切り替えを実装していきます。
タブの切り替えメニューを実装する流れ
ここでは以下を前提として話を進めていきますので、実装前に確認および準備しておいてください。
- Stimulusコントローラー作成済み
- CSSフレームワークはTailwindを使用(導入済み)
ちなみに、Stimulusコントローラーは$ rails g stimulus [コントローラー名]
コマンドを実行すると、
app/javascript/controllers
以下に[コントローラー名]_controller.js
という名称で自動生成されます。
それではいきます。
ナビゲーションメニューを作成
今回、ナビゲーションメニュー(navbar)のコードは公式ページのサンプル集から引用してアレンジしていくことにします。
app/views/layouts
以下に_navigation.html.erb
を新規作成し、サンプル集から引用したコードを貼り付けます。
コードは今回のデモ作成用に少しアレンジしています。
<div class="min-h-full">
<nav class="bg-gray-800">
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div class="flex h-16 items-center justify-between">
<div class="flex items-center">
<div class="flex-shrink-0">
<img class="h-8 w-8" src="https://tailwindui.com/img/logos/mark.svg?color=indigo&shade=500" alt="Your Company">
</div>
<div class="hidden md:block">
<div class="ml-10 flex items-baseline space-x-4">
<!------------- 今回、アレンジした箇所↓↓ -------------->
<a class="is-active" aria-current="page" data-navbar-target="menu" data-action="navbar#menuClick">ダッシュボード</a>
<a class="not-active" data-navbar-target="menu" data-action="navbar#menuClick">メニュー1</a>
<a class="not-active" data-navbar-target="menu" data-action="navbar#menuClick">メニュー2</a>
<a class="not-active" data-navbar-target="menu" data-action="navbar#menuClick">メニュー3</a>
<!--------------------- ここまで ---------------------->
</div>
</div>
</div>
・・・(以下省略)・・・
</div>
</div>
</nav>
</div>
application.html.erbを編集
app/views/layouts/application.html.erb
を以下のように編集します。
<!DOCTYPE html>
<html>
<head>
・・・(省略)・・・
</head>
<body data-controller="navbar">
<div class="min-h-screen bg-gray-100">
<%= render 'layouts/navigation' %>
<div class="container mx-auto px-8 py-3">
<%= yield %>
</div>
</div>
</body>
</html>
ここで、あらかじめ作成しておいたStimulusコントローラーnavbar_controller.js
がbody全体に適用されるようにするため、bodyタグにdata-controller="navbar"
を追記しています。
コンテンツ表示用のビューを作成する
メニュータブの切り替えと連動して表示されるコンテンツ用のビューを作成します。
今回は簡単なデモということで、以下のようにシンプルなコードにしました。
<h1 class="text-3xl mb-3">トップページ</h1>
<div>
<div data-navbar-target="content">
<p class="mb-3">ダッシュボード</p>
<%= image_tag "dashboard.png", class: "w-full" %>
</div>
<div class="hidden" data-navbar-target="content">
<p class="mb-3">コンテンツ1</p>
<%= image_tag "content01.png", class: "w-full" %>
</div>
<div class="hidden" data-navbar-target="content">
<p class="mb-3">コンテンツ2</p>
<%= image_tag "content02.png", class: "w-full" %>
</div>
<div class="hidden" data-navbar-target="content">
<p class="mb-3">コンテンツ3</p>
<%= image_tag "content03.png", class: "w-full" %>
</div>
</div>
ちなみに、image_tag
ではapp/assets/images
以下に保存しておいた画像を呼び出しています。
Stimulusコントローラーを編集
最後にStimulusコントローラーにJavaScriptコードを書いていきます。
import { Controller } from "@hotwired/stimulus"
// Connects to data-controller="navbar"
export default class extends Controller {
static targets = ["menu", "content"]
/* メニュータブをクリックしたら発火するアクション */
menuClick(event){
const menus = this.menuTargets // メニューバー全体の要素
const current = event.currentTarget // クリックしたメニューバーの要素(現在地)
const currentIndex = menus.indexOf(current) // クリックしたメニューバーのインデックス番号
const contents = this.contentTargets // コンテンツ全体の要素
menus.forEach((menu, index)=>{ // 初期化
if(current.classList.contains("not-active")){
menu.classList.remove("is-active")
menu.classList.add("not-active")
contents[index].classList.add("hidden")
}
})
if(current.classList.contains("not-active")){ // クリックしたメニューのclassをアクティブにする
current.classList.remove("not-active")
current.classList.add("is-active")
contents[currentIndex].classList.remove("hidden") // クリックした要素のコンテンツを表示する
}
}
}
以上、これで出来上がりイメージのような動作になればOKです。
コメント