はじめに
この記事では、Ruby on Rails 7を使用してフロントエンドのJavaScriptのフレームワークであるHotwireのStimulusの使用するまでの流れを追っていきます。
Stimulusは既に生成されたHTMLに対して部分的にJavaScriptを使用するケースに適したフレームワークになります。JavaScriptを使用してボタンのクリックなどの処理を作成していく際には、onclickイベントなどを
直接記述する方法からjQueryを利用するなど様々な方法があると思いますが、書き方を合わせるためにもフレームワークとしてStimulusを使うとコードの把握も行いやすく、更に便利になるのではないかと思います。
特徴
Stimulusの特徴としては、HTML側のタグの属性に適用するStimulusのクラス(controller: Railsとは別)や動作(action)を書いていき、同じ動作を行う箇所については再利用しやすいという点があります。特徴については、Stimulusのドキュメントにも説明がありますのでそちらも合わせて読んでいただければと思います。
使用開始までの流れ
最初にRails 7をインストールします。以下のコマンドでRuby on Railsをインストールすることができます。
gem install rails
新しくプロジェクトを作成する場合には次のコマンドで作成することができます。
rails new my_great_app
もしapp/javascriptのディレクトリが存在しない時には
既存のプロジェクトがあり、そのプロジェクトを作成する時のrails new
で-B
オプションなどを使用してbundle install
をスキップした場合にはStimulusの環境が整っていないため追加で次のコマンドを実行する必要があります。
bundle install #必要であれば
bin/rails importmap:install
bin/rails stimulus:install
ここではnodeを使用せずにimportmapを使用するようにしています。
Stimulusを使用してボタンの動作を定義する
さっそくStimulusを使用してみようと思います。まずは、何か表示するページが必要なため次のコマンドでRailsのコントローラーとビューを作成します。
bin/rails generate controller Products index
bin/rails server
(ブラウザでhttp://localhost:3000/products/indexを開いて表示されることをチェック)
これで使用開始までの準備を整いました。今回は、ボタンとテキストを表示するエリアを用意してボタンを押したらテキストが変わるようにしていきたいと思います。
ボタンを作成します。
<div>
<button>Push me</button>
<span>hello!</span>
</div>
これで画面にボタンと初期のメッセージが表示されると思います。しかし、まだこれではStimulusとの連携を取ることができません。Stimulusに関連付けるには次のように<div>
の属性を追加します。
<div data-controller="hello">
<button>Push me</button>
<span>hello!</span>
</div>
この変更で先ほどページをリロードするとHello World!
と表示されていると思います。
表示されていればStimulusとの連携ができています。しかし、残念なことにボタンが消えてしまいました。
ボタンが消えてしまったのは、Stimulusの例としてあらかじめ定義されているStimulusのJavaScriptのコードの動作の影響になります。以下のファイルになります。
import {Controller} from "@hotwired/stimulus"
export default class extends Controller {
connect() {
this.element.textContent = "Hello World!"
}
}
erbのファイルでdiv
タグの属性でdata-controlelr="hello"
を設定することでStimulusのコントローラー(Railsのとは別になります)のhello_controller.js
が自動的に関連付けられました。この属性での指定の仕方とファイル名のつけ方については、公式ドキュメントのコントローラーの説明により詳しく書かれております。
また、このStimulusのコントローラーのconnect()
で中身をHello World!に書き換えていますのでボタンが消えてしまいました。
ボタンの動作を設定していきたいと思います。erb側は次のようになります。
<div data-controller="hello">
<button data-action="click->hello#show">Push me</button>
<span data-hello-target="text">hello!</span>
</div>
Stimulusのコントローラー側は次のようになります。
import {Controller} from "@hotwired/stimulus"
export default class extends Controller {
static targets = ["text"]
show() {
this.textTarget.textContent = "Hello World!"
}
}
erbの方でボタンには、data-action
の属性を追加して、そこに{イベント名}->{コントローラー名}#{メソッド名}
の形式で動作を定義しています。また、Stimulusのコントローラークラスからアクセスしたいタグをdata-hello-target
のようにdata-{コントローラー名}-target
の形式でアクセスしたい名前を設定(ここではtext
)しています。この詳細については公式のドキュメントの説明に詳しく書かれております。
StimulusのコントローラーのJavaScriptの方では、呼び出されるshowメソッドを追加しています。また、staticのtarget変数にアクセスしたいタグで指定した名前のtextを定義しています。showメソッドでは、this.textTarget.textContent
でタグの中身にアクセスしています。this.{ターゲット名}Target
は自動的に作られています。
textTargetで使用しているtextContentはDOMのノードのプロパティになります。このためthis.textTarget.innerText
のようにしてもテキストを書き換える事ができます。
変数を利用して状態を管理してみる
変数(Stimulusの機能のvalues)を使用して状態を管理した例を試していきます。Stimulusにはタグの属性で指定した名前の変数をJavaScript側でも利用しやすい仕組みが用意されています。
ここでは、ボタンを押されたときに数値が加算され、その値を表示する例を作ってみます。数値は、pushという名前で扱うことにします。
<div data-controller="hello" data-hello-push-value="1">
<button data-action="click->hello#add">Push me</button>
<span data-hello-target="text"></span>
</div>
このHTML側には、data-controller
の属性を設定したタグと同じタグにdata-hello-push-value
を追加しています。ここで初期値の1を設定しています。
JavaScript側のコードは次のようになります。
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = ["text"]
static values = { push : Number }
initialize() {
this.render()
}
add() {
this.pushValue++
this.render()
}
render() {
this.textTarget.textContent = this.pushValue
}
}
static values=
でpush
を使用するように設定しています。また、ここではNumberという型を指定しています。画面に表示する時には、this.pushValue
のようにValueを名前(ここではpush)の後にくっつけた変数名(this.{名前}Value)でアクセスすることができます。HTML側で初期値も設定できるため便利だと思います。
おわりに
Stimulusを利用した例を紹介させていただきました。共通の書き方の流れでJavaScriptを書いていけるのは便利な仕組みなのではないかと思います。また、Stimulusのコントローラーという形でクラスを作成しておけば再利用もし易いのではないかと思います。
そして、どこのタグでStimulusが利用されているのかどうかも属性(data-controller)を探していけば良さそうなので、対応箇所を見つけやすさという点でも便利なのではないかと思います。