執筆時点で昨夕Stimulus v3.2.2がリリースされました。
パッチバージョンではありますが、内容がパッチバージョンとは言えないものが多いので一応拾っておこうと思います。
機能の追加・変更
keyboard event filterにPageUp
/PageDown
が追加された
Stimulusにはもともとキーボードイベントのフィルタリング機構があります。
下記のようにアクション記述のイベント名を書くところのキーボードイベント(keydown,keyup,keypress)にドットで続けてキー名を書けば、対象のキーが押された時だけ実行されるアクションとして定義することができる機能です。
<xxx data-action="keydown.left->tab#prev">
そこにPageUp
、PageDown
のキーをデフォルトで指定できるようになりました
構文中ではpage_up
, page_down
という名前で指定するようです。
キーボード操作を実現する際、PageUp
/PageDown
ではなく ArrowUp
/ArrowDown
を使うケースが多いのですが、Web特有のUIなどでたまに求められることもあるのでデフォルトで用意しよう...ということなのかなと思います。
参考(Feedパターンなど):
Number型のValueの値に_
を含めることができるようになった
StimulusにはHTML側からStimulusコントローラ内で参照したい値を受け渡せるようにするためのValuesと呼ばれるAPIがあります。
<div data-controller="foo" data-foo-some-value="123" />
static values = {some: Number}
connect() {
alert(this.someValue); // 123
}
コントローラ側で指定した型指定(上記のコードだとNumber)に従ってAPI(this.someValue)にアクセスした際にパースされた結果を得ることができるようになっています。
今回の変更はこのHTML側の記述で、Number型の値の記述だけ_
を含めることができるようになったというものです。
主に桁を把握しやすく記述するための配慮のようのです
<div data-controller="foo" data-foo-some-value="123_456" />
See the Pen Untitled by Takuya Nakajima (@Takuya-Nakajima) on CodePen.
Clickイベントにもフィルタリング機構が追加された
冒頭の差分紹介時にキーボードイベントのフィルタリングの機構があるという話をしましたが、それと似た機能がクリックイベントにも追加されました。
主にコントロールキーを押しながらのクリックや、シフトキーを押しながらのクリックなどをサポートする目的の変更になります。
対応しているキーはmeta
, ctrl
, alt
, shift
の4キーのようです。
キーの組み合わせは+
を使います。
<button type="button"
data-controller="foo"
data-action="ctrl+click->foo#notify"
/>
outletで指定されたセレクタが変更された際にoutletAPIの参照するコントローラも対応したものになるようになった。
Stimulusには要素に指定されたコントローラを使役的に操作したい目的でコントローラのインスタンスにアクセスするためのOutletと呼ばれるAPIが存在しています。
下記のコードのようにfooコントローラを割り当てた要素にhogeコントローラを割り当てた要素のセレクタをdata-{自コントローラ}-{参照したいコントローラ}-outlet="{セレクタ}"
という記述で指定すると{参照したいコントローラ名}Outlet
というプロパティ名でアクセスできるようになります。
<div
data-controller="foo"
data-foo-hoge-outlet="#hoge"
/>
<div
id="hoge"
data-controller="hoge"
/>
このoutlet記述で指定しているセレクタ(上記のコードでは#hoge
)が変更された際、これまではその変更が反映されることはなかったのですが、今回の変更によって反映されるようになりました。
See the Pen Untitled by Takuya Nakajima (@Takuya-Nakajima) on CodePen.
afterLoadハンドラ内のthisが未定義だったバグが治った
Stimulusにはコントローラが定義(application.register
)されたタイミングでafterLoadと呼ばれる関数を発火させるという機能があります(要素にアタッチするタイミングではないことに注意)
このafterLoad内でのthis
がundefined
になっていたバグがあったのですが、これが解消されてコントローラクラスを指すようになりました。
afterLoadハンドラの第二引数にapplication
インスタンスが渡るようになった
上述のafterLoadハンドラは第一引数に定義された時のコントローラ名がわたるのですが、今回それに加えapplication(Application.startの返り値)が渡るようになりました。
上述のafterLoadのコンテキスト変更の差分にしれっと入っており意図は語られていないのですが、サンプルコードにapplicationインスタンスを利用した実装があります。
この例では特定のクラスにマッチする要素にコントローラを割り当てたいが、data-controller
というキーワードはapplication.schemaで変更可能なので信頼性を高めるためにapplication.schemaを参照してキーワードを取得する...といった目的で使われてるようです。
稀に嬉しいことがあるのかな〜という感想です。
outletAPIをconnect内で参照した際に、参照したコントローラが未定義になる問題が一定解消した。
多分これが今回の本命のPRなのかなと思います。
要素の解釈順の問題で以下のような構造ではconnectのタイミングでoutletを正しく参照できませんでした。
<div controller="foo" data-foo-hoge-outlet="#hoge">
<div controller="hoge" id="hoge">
</div>
</div>
application.register('foo', class extends Controller) {
static outlets = ['hoge'];
connect() {
alert(this.hogeOutlet); // undefined
}
}
これはコントローラの割り当てが外の要素から始まることに起因しており、fooコントローラがアタッチされたタイミングではまだ#hogeにhogeコントーラがアタッチされていないため、fooのconnect内では参照が失敗するというものになります。
必然といえば必然なのですが、この挙動に対するissueが乱立したこともあって、一定の対応がとられたようです。
対応内容はoutlet参照時に対象のコントローラが存在しなかった場合、子要素を探索して要素をアタッチするという処理を割り込ませるというものになります。
これにより、上記のコードでも正常にhoge
コントローラにアクセスできるようになります。
ただ、Stimulusをrailsで利用していたり、またそれを参考にしてautoloadingしている場合、アタッチは非同期的になるのでかわらず動作は保証されないことになります。
ということで本解消というよりは一定の解消を目指した変更という感じでしょうか。
簡単にawaitするための機構を用意するとかそういう対応がとられるのかなと思っていたので少し驚きです。
下記はoutletで参照してるコントローラのidentifierをconnect時に書き出す処理をバージョン比較したものです。
stimulus3.2.1までの挙動
See the Pen Untitled by Takuya Nakajima (@Takuya-Nakajima) on CodePen.
stimulus3.2.2からの挙動
See the Pen Untitled by Takuya Nakajima (@Takuya-Nakajima) on CodePen.
registerActionOptionのコールバックに渡ってくるeventオブジェクトのparamsフィールドが参照できるようになる
Stimulusのイベントのフィルタリング機構にActionOptionと呼ばれるものがあります。
このActionOptionは独自定義することもでき、たとえばdetails要素の開閉イベントのtoggle
を、openした時だけに限定してアクションを実行させたい時、以下のようなActionOptionを定義しておくと簡単に実現できます。
application.registerActionOption("open", ({ event }) => {
if (event.type == "toggle") {
return event.target.open == true
} else {
return true
}
})
<details
data-controller="foo"
data-action="toggle->foo#notify:open"
/>
このActionOptionを定義するregisterActionOption
関数は第一引数にActionOption名、第二引数に登録するフィルタリング関数を定義するのですが、このフィルタリング関数に渡る引数オブジェクト内のイベントプロパティに変更がありました。
もともとイベントプロパティにはイベントオブジェクトが渡ってきており、そこに変更はないのですが、StimulusのActionParams(event.params)が利用可能な状態となって渡ってくるようになりました。
See the Pen Untitled by Takuya Nakajima (@Takuya-Nakajima) on CodePen.
registerActionOptionのコールバックに渡ってくるデータにコントローラのインスタンスが追加される
前述のregisterActionOption
のコールバック関数の引数オブジェクトにcontroller
フィールドが追加され、このイベントを受信しているコントローラのインスタンスが渡るようになりました。
<button type="button" data-action="click->some#method:option">
上記のような記述の場合、controllerフィールドにはsome
コントローラのインスタンスが渡ります。
これにより、コントローラ側のvaluesAPIなどにアクセスできるようになるため、より柔軟なフィルタリングが可能になります。
See the Pen Untitled by Takuya Nakajima (@Takuya-Nakajima) on CodePen.
上記はくじ引きアプリケーションです。
あたりくじ(5)を引き当てたときだけalertがでるようなフィルタリングをしており、当たりくじの値はvaluesAPIを使って柔軟に設定できるようになっています。
ただ、この機能を使うと特定のコントローラに依存したActionOptionとなりやすいので、関連性をわかりやすくするために前述のafterLoad内で定義するなどしたほうがよいかもしれませんね。(ちょうどapplicationインスタンスにもアクセスできるようになってるので)
終わりに
やはりどうみてもパッチバージョンの変更じゃないですねw
outletの挙動変更(主にconnect内での動作保証)が注目度の高い変更なのかなと思いますが、autoloader対応など課題は残されているので今後に注目です。
個人的にはActionOptionに渡るイベントオブジェクトのparamsが解釈可能になった変更が一番ありがたい変更だったかな〜と思います。