Hotwire勉強会ログ (2)

# はじめに

この記事では、Ruby on Rails 7を使用してフロントエンドのJavaScriptのフレームワークであるHotwireのStimulusの使用するまでの流れを追っていきます。

Stimulusは既に生成されたHTMLに対して部分的にJavaScriptを使用するケースに適したフレームワークになります。JavaScriptを使用してボタンのクリックなどの処理を作成していく際には、onclickイベントなどを
直接記述する方法からjQueryを利用するなど様々な方法があると思いますが、書き方を合わせるためにもフレームワークとしてStimulusを使うとコードの把握も行いやすく、更に便利になるのではないかと思います。

## 特徴

Stimulusの特徴としては、HTML側のタグの属性に適用するStimulusのクラス(controller: Railsとは別)や動作(action)を書いていき、同じ動作を行う箇所については再利用しやすいという点があります。特徴については、[Stimulusのドキュメント](https://stimulus.hotwired.dev/handbook/introduction#about-stimulus)にも説明がありますのでそちらも合わせて読んでいただければと思います。

# 使用開始までの流れ

最初にRails 7をインストールします。以下のコマンドでRuby on Railsをインストールすることができます。

```
gem install rails
```

新しくプロジェクトを作成する場合には次のコマンドで作成することができます。

```
rails new my_great_app
```

## もしapp/javascriptのディレクトリが存在しない時には

既存のプロジェクトがあり、そのプロジェクトを作成する時の```rails new```で```-B```オプションなどを使用して```bundle install```をスキップした場合にはStimulusの環境が整っていないため追加で次のコマンドを実行する必要があります。

```bash
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を開いて表示されることをチェック)
```

これで使用開始までの準備を整いました。今回は、ボタンとテキストを表示するエリアを用意してボタンを押したらテキストが変わるようにしていきたいと思います。
ボタンを作成します。

```erb:app/views/products/indes.html.erb
<div>
  <button>Push me</button>
  <span>hello!</span>
</div>
```

これで画面にボタンと初期のメッセージが表示されると思います。しかし、まだこれではStimulusとの連携を取ることができません。Stimulusに関連付けるには次のように```<div>```の属性を追加します。

```erb:app/views/products/index.html.erb
<div data-controller="hello">
  <button>Push me</button>
  <span>hello!</span>
</div>
```

この変更で先ほどページをリロードすると```Hello World!```と表示されていると思います。
表示されていればStimulusとの連携ができています。しかし、残念なことにボタンが消えてしまいました。

ボタンが消えてしまったのは、Stimulusの例としてあらかじめ定義されているStimulusのJavaScriptのコードの動作の影響になります。以下のファイルになります。

```js:app/javascript/controllers/hello_controller.js
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```が自動的に関連付けられました。この属性での指定の仕方とファイル名のつけ方については、[公式ドキュメントのコントローラーの説明](https://stimulus.hotwired.dev/reference/controllers#identifiers)により詳しく書かれております。

また、このStimulusのコントローラーの```connect()```で中身をHello World!に書き換えていますのでボタンが消えてしまいました。

ボタンの動作を設定していきたいと思います。erb側は次のようになります。

```erb:app/views/products/index.html.erb
<div data-controller="hello">
  <button data-action="click->hello#show">Push me</button>
  <span data-hello-target="text">hello!</span>
</div>
```

Stimulusのコントローラー側は次のようになります。

```js:app/javascript/controllers/hello_controller.js
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```)しています。この詳細については[公式のドキュメント](https://stimulus.hotwired.dev/reference/actions)の説明に詳しく書かれております。

StimulusのコントローラーのJavaScriptの方では、呼び出されるshowメソッドを追加しています。また、staticのtarget変数にアクセスしたいタグで指定した名前のtextを定義しています。showメソッドでは、```this.textTarget.textContent```でタグの中身にアクセスしています。```this.{ターゲット名}Target```は自動的に作られています。

textTargetで使用しているtextContentはDOMのノードのプロパティになります。このため```this.textTarget.innerText```のようにしてもテキストを書き換える事ができます。

# 変数を利用して状態を管理してみる

変数(Stimulusの機能のvalues)を使用して状態を管理した例を試していきます。Stimulusにはタグの属性で指定した名前の変数をJavaScript側でも利用しやすい仕組みが用意されています。
ここでは、ボタンを押されたときに数値が加算され、その値を表示する例を作ってみます。数値は、pushという名前で扱うことにします。

```erb:app/views/products/index.html.erb
<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側のコードは次のようになります。

```js:app/javascript/controllers/hello_controller.js
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)を探していけば良さそうなので、対応箇所を見つけやすさという点でも便利なのではないかと思います。


# 参考
- [Railsプロジェクト作成](https://qiita.com/yuitnnn/items/b45bba658d86eabdbb26)
- [Stimulus](https://stimulus.hotwired.dev/)