Stimulus.JS: 値の変更時にトリガーされるコールバックをコントローラで利用する(翻訳)
Stimulusのvalues APIには、「変更時に発火するコールバック」という便利な機能があります。これによって、値が変更されたときに常に応答できるようになります。🦉
values APIについて簡単におさらいしておくと、Stimulusのコントローラ要素にあるdata-*
属性を読み書きできるようにするものです。
たとえば、HTMLで<div data-controller="counter" data-counter-time-value="10">10</div>
のように定義できます。
次に、それらをStimulusコントローラのstatic values
で以下のように定義します。
export default class extends Controller {
static values = { time: Number }
}
上のStimulusコントローラで、以下のようにconnect
関数でタイマーを開始したくなるかもしれません。
// app/javascripts/controllers/counter_controller.js
export default class extends Controller {
static values = { time: Number }
connect() {
this.timer = setInterval(() => {
this.timeValue--;
if (this.timeValue > 0) {
this.element.textContent = this.timeValue;
} else {
this.element.textContent = "Time's up!"
clearInterval(this.timer);
}
}, 1000);
}
}
上のような実装は問題なさそうに見えるかもしれませんが、私は関数のスコープを小さく保つのが好みなので、次の要件として停止(stop)と一時停止(pause)機能を追加することにします(さらに、変更コールバックの利用方法を説明するためのブリッジも必要でしょう)。
そこで、変更コールバックを用いて少しリファクタリングしてみましょう。
export default class extends Controller {
static values = { time: Number }
connect() {
this.timer = null;
this.#start();
}
// private
timeValueChanged() {
if (this.timeValue > 0) {
this.element.textContent = this.timeValue;
} else {
this.element.textContent = "Time's up!";
clearInterval(this.timer);
}
}
#start() {
this.timer = setInterval(() => {
this.timeValue--;
}, 1000);
}
}
timevalueChanged()
は、this.timeValue
が変更されるたびに呼び出されます(変更はprivate関数#start()
で行われます)。
皆さんは、このコードの方が良いと思いますか?「クラスが肥大化している」という意見も聞こえてきそうですが、私はこの方が追いかけやすいと思います。
#start
関数が関連しているのはタイマーの開始だけです。
ここに、stop()
やpause()
といったpublic関数を併記することで以下のような処理が行えることは、すぐ想像がつくでしょう。
- 文字カウントで許される最大数を表示する
- urlValueが変更されたらAPIエンドポイントをフェッチする
- select要素で選択したオプションを表示する
- 値の「読み込み中」はボタンを無効にする
これはStimulusに隠れているさまざまなヘルパー関数の1つであり、このヘルパー関数を知っておけばさまざまな場面で有用に使えます。
皆さんは変更コールバックをどんなふうに活用していますか?よろしければ私にも教えてください。
概要
元サイトの許諾を得て翻訳・公開いたします。
日本語タイトルは内容に即したものにしました。