簡単Stimulus

この記事は ケーシーエスキャロット Advent Calendar 2023 の記事です。

先日お仕事でStimulusを扱う機会があり、 最初こそとっつきにくいと感じましたが、割とスムーズに理解することができました。 ちょうど良い機会なので、Stimulusの使い方をまとめてみようと思います。

と言っても基本的な使い方は Stimulus Handbook にまとまっているので、 この記事では実際に実装しながら使い方を簡単に振り返ってみようと思います。

注意

  • この記事ではStimulusの導入方法は扱いません

作るもの

更新ボタンを押すと、APIから現在時刻を取得し、画面に表示する機能をStimulusで実装します。
イメージは以下です。

今回は段階を追って進めていきます。

準備

view と stimulus controller を用意します。

view

こんな感じのviewを用意します。

<div>
  <button>更新</button>
  <div>
    <%= Time.current %>
  </div>
</div>

Stimulus controller

Stimulus の controller は bin/rails g stimulus コマンドを使うと、Stimulus の controller ファイルの作成と、読込設定まで追加してくれます!(便利)
今回は update-button という名前の controller を作成するので、コマンドは以下になります。

bin/rails g stimulus update_button

※update-button という命名はあまり適していないような気がしますが、スルーしてください。

作成された app/javascript/controller/update_button_controller.js はこんな感じになっていると思います。

import { Controller } from "@hotwired/stimulus"

// Connects to data-controller="update-button"
export default class extends Controller {
  connect() {
  }
}

1: 更新ボタンにクリックイベントを設定する

いきなり全てを実装するのも難しいので、まずは更新ボタンにクリックイベントを設定するところから。
更新ボタンをクリックすると alert ダイアログを表示するようにしてみます。

view と Stimulus controller を以下のように書き換えます。

<div data-controller="update-button">
  <button data-action="click->update-button#update">更新</button>
  <div>
    <%= Time.current %>
  </div>
</div>
export default class extends Controller {
  update() {
    alert("update!!!")
  }
}

view の この部分。

<div data-controller="update-button">
  <button data-action="click->update-button#update">更新</button>

一番上の div の data-controller で適応させる Stimulus の controller を指定します。
※controller のファイル名は _ だが、controller 名としては - を使うので注意

また、button には data-action でイベントと、イベント発火時に実行する処理を設定しています。
この場合は、クリックイベント発火時に、 update-button controller の update というメソッドを実行する、という設定になります。

2: 更新ボタンクリック時に時刻表示を書き換えるようにする

次は実際に画面の表示を書き換えられるようにします。
更新ボタンをクリックすると、日時表示の要素の innerHTML を「更新した!」という文字で更新します。

書き換え先の要素は getElementByID などを使って取得することもできますが、 Stimulus には要素を参照するための target という属性があるのでこれを使います。

view と Stimulus controller を以下のように書き換えます。

<div data-controller="update-button">
  <button data-action="click->update-button#update">更新</button>

  <!-- ここを書き換える -->
  <div data-update-button-target="area">
    <%= Time.current %>
  </div>
</div>
export default class extends Controller {
  // area という target を定義
  static targets = ["area"]

  update() {
    this.areaTarget.innerHTML = "更新した!"
  }
}

書き換え先の要素を area という名前の target で指定させるようにしました。 この area はどこを参照するかというのは view で、data-controller名-target という属性で指定します。

  <div data-update-button-target="area">
    <%= Time.current %>
  </div>

area target は controller 側で this.[target名]Target とすると参照することができます。
この場合 this.areaTarget で、areatargetに指定した要素が参照可能です。

3: APIから取得した値で時刻表示を書き換えるようにする

次は実際APIを呼び出して、取得した値で時刻表示を書き換えるようにします。

使用するAPIは、 /api/time/now で呼び出すと、現在時刻をテキストで返してくれるものとします。
実装はこんな感じ。

class Api::TimeController < ApplicationController
  def now
    render plain: Time.current
  end
end

API の URL は Stimulus controller 内に直接定義することもできますが、 またまた Stimulus には value という、値を受け渡すのに便利な属性があるので、 こちらを利用します。

view と Stimulus controller を書き換えます。

<div data-controller="update-button" data-update-button-url-value="/api/time/now">
  <button data-action="click->update-button#update">更新</button>
  <div data-update-button-target="area">
    <%= Time.current %>
  </div>
</div>
export default class extends Controller {
  static targets = ["area"]
  // url という value を定義
  static values = { url: String }

  update() {
    fetch(this.urlValue, { method: "GET" })
      .then((response) => response.text())
      .then((time) => this.areaTarget.innerHTML = time)
  }
}

API の URL を url というvalueで渡せるようにしました。
value が参照する値の指定も同様に view 側で行います。
値の指定は data-controller名-value名-value という属性で行います。

<div data-controller="update-button" data-update-button-url-value="/api/time/now">

data-controller と同じ要素に指定する必要があるので注意

url value は controller 側で this.[value名]Value とすると参照することができます。
この場合 this.urlValue で、指定した API の URL が参照できます。

これで完成です!

まとめ

  • data-controller で適応させる controller を指定
  • data-action で要素にイベントを設定できる
  • 他の参照したい要素は target 、値を参照した場合は value が使える