patorashのブログ

方向性はまだない

stimulus 2.0.0の進化が凄い件(サンプルコードあり)

仕事で、とあるRailsアプリを作っているのですが、そこでstimulusを採用していました。そうしたらちょうど少し前にstimulusのバージョン2.0.0がリリースされていました。このバージョンアップによって、かなり書きやすくなりました。

今までと何が違うのか?

今までは、コントローラーを指定してから、そのコントローラーのターゲットを指定するのが面倒でした。

before

<div data-controller="vote">
  <button type="button" data-target="vote.button">投票</button>
</div>

after

属性名だけでどのコントローラーのターゲットか分かるので読みやすくなりました。

<div data-controller="vote">
  <button type="button" data-vote-target="button">投票</button>
</div>

新しく追加された機能

大きく変わる機能が、Value APICSS Class APIです。

Value API

Value APIは、そのまんまですが、値を持つことができます。 stimulusの公式サイトのリファレンスから引用しますと、以下のような感じです。

html

<div data-controller="loader"
     data-loader-url-value="/messages">
</div>

JavaScript

import { Controller } from "stimulus"

export default class extends Controller {
  static values = { url: String }

  connect() {
    fetch(this.urlValue).then(/* … */)
  }
}

この例だと、fetchする先のURLをValue APIで変えることができる、という感じです。valueの指定は、Hashのキーにvalueの名前を、値に型を書きます。そうすると、読み込んだ際に型に沿った形にキャストしてくれます。

これだけだと、値を渡せるようになっただけ?という感じですが、それだけではありません!Value APIは、値が変わった時にコールバック関数が呼ばれます。 CodePenで、Value APIと、そのコールバックを使って、送信ボタンの状態制御をしてみました。

See the Pen stimulus v.2.0.0 sample(Value API) by patorash (@patorash) on CodePen.

stimulusのコントローラーでinitialize, connect, disconnectの3つのメソッドが自動でコールバックされます。今回はこれらも使って、

  1. connectのタイミングでコメント入力エリアにinputのイベントリスナを定義する。
  2. 入力されたら、handleEventメソッドでイベントを拾い、入力エリアに1文字以上あったら、enableValueをtrueに変更。0文字ならfalseに変更。
  3. enableValueの値が変わったらenableValueChangedメソッドがコールバックで呼ばれ、submitボタンのdisabledの状態が変わる!
  4. disconnectのタイミングでイベントリスナを解除する。

という仕組みになっています。valueはhtmlの要素の属性として値がある状態なので、戻るボタンなどで戻った時は再びconnectが呼ばれてちゃんと動く、というわけです。

私的には非常にわかりやすくていいなぁ!と思いました。

4時を過ぎたので、今日はこの辺りまでにして、続きのCSS Class APIはこの記事に追記していきます。

続きを書いていきます!

CSS Class API

CSS Class APIは、stimulusで使うCSSに名前をつけることができる機能です。 stimulusの公式サイトのリファレンスから引用しますと、以下のような感じです。

JavaScript

// controllers/search_controller.js
import { Controller } from "stimulus"

export default class extends Controller {
  static classes = [ "loading" ]

  loadResults() {
    this.element.classList.add(this.loadingClass)

    fetch(/* … */)
  }
}

html

<form data-controller="search"
      data-search-loading-class="search--busy">
  <input data-action="search#loadResults">
</form>

アクションのloadResultsメソッドを呼び出したら、searchコントローラーの紐づいたformのcss.search--busyを追加します。

以前までは、クラス名をJavaScriptにハードコーディングしなければいけませんでした。

// 以前のstimulus
// controllers/search_controller.js
import { Controller } from "stimulus"

export default class extends Controller {

  loadResults() {
    this.element.classList.add('search--busy') // => ハードコーディング

    fetch(/* … */)
  }
}

これだと、CSSのクラス名が変わった場合にstimulusのコードに修正が必要になっていました。しかし、CSS Class APIの登場により、HTML側の修正だけでよくなりました。

CSS Class APIが活用できる具体的なシーン

例えばBootstrapのバージョンが上がったときを例にしましょう。

未だにBootstrap3を使い続けている環境があるとします(私の担当プロジェクトですが…)。そこでは、要素を隠すためのCSS Class名は、.hiddenです。

Bootstrap3を使っているフォーム

<div data-controller="comment"
    data-comment-close-class="hidden">
  <button type="button"
      data-action="comment#openForm">コメントする場合は押してください</button>
  <form class="hidden" onSubmit="return false"
      data-comment-target="form">
    <input type="text" name="content">
    <input type="submit" value="送信">
    <button type="button"
        data-action="comment#closeForm">キャンセル</button>
  </form>
</div>

JavaScript

// controllers/comment_controller.js
import { Controller } from "stimulus"

export default class extends Controller {
  static targets = [ "form" ]
  static classes = [ "close" ]

  openForm() {
    this.formTarget.classList.remove(this.closeClass)
  }

  closeForm() {
    this.formTarget.classList.add(this.closeClass)
  }
}

これをBootstrap4にバージョンアップします。 data-comment-close-class="d-none"と、<form class="d-none">に修正します。

<div data-controller="comment"
    data-comment-close-class="d-none">
  <button type="button"
      data-action="comment#openForm">コメントする場合は押してください</button>
  <form class="d-none" onSubmit="return false"
      data-comment-target="form">
    <input type="text" name="content">
    <input type="submit" value="送信">
    <button type="button"
        data-action="comment#closeForm">キャンセル</button>
  </form>
</div>

stimulus側のコードは、変更することは何もありません!

このように、CSS Frameworkに依存しない形でJavaScriptのコーディングが可能になりました。

まとめ

stimulusが登場した時点でも、すごい!とは思っていたのですが、バージョン2になって、さらに使いやすくなりました!Value APIの登場で、他のMVVMライブラリのcomputedみたいなことがやりやすくなりましたし、CSS Class APIの登場で、CSS Frameworkに依存しないコーディングができるようになりました。

最近のフロントエンドとバックエンドを疎結合にしていく流れも、もちろんいいとは思いますが、少々(かなり?)複雑です。Railsで簡単なものを簡単に実装しやすいという点では、stimulusは最高のパートナーになってくれると思います。