Riot.jsに触れてみた話 Riot.jsの文法というか書き方
※2017年10月19日 追記
本記事のriot.jsは、バージョンは2のものとなります。
現在の最新バージョンは3.7.3
ですので、参考にならないこともありますので、ご注意ください。
はじめに
前回の続きで、今回はもっと具体的にRiot.jsの書き方に触れてみる。
※よくあるチュートリアルのようなものを書く予定なので、自分の備忘録的に書きます。 したがって、随時更新の形にします。
cognitomさんの記事にRiot.jsのソースコードをとても詳しく解説されているので、 細かい仕様まで確認したい方はご参照ください。
書き方
opts変数
前回の最期でもちょっとだけ書いたopts
変数について。tagファイル内で使われる変数だが、本当に中身はこれだけなのか疑問に思ったので、コンソールにopts
変数の中身を出力してみた。
マウントした値は以下。
<script> riot.mount('app',{ title: 'Hello Riot.js!', items: [1, 2, 3], text: 'foo'} ) </script>
結果は以下。
本当にマウントした値のみだったので初期化用かな。ちなみに、同じtagファイル内に記述しているのであれば、アクセスはもちろんthis
を使う。
変数、メソッドの指定
Riot.jsでは、変数やメソッドを{}
で指定する。この後よく出てくるので、そういうものと思っていただければ。
とりあえず変数の例を載せる。
<app> <h1>{ opts.title }</h1> <p>{ message }</p> <script> this.message = "hogehoge"; </script> </app>
イベントハンドラ
ボタンをクリックした時などのイベントハンドラの書き方について。
例は一番多そうなボタンクリックonclick
の場合。
<app> <h1>{ opts.title }</h1> <button onClick={ check }>チェック</button> <script> check(e) { // チェックボタンが押された時の処理 } </script> </app>
もう一個、サブミットonSubmit
の場合。
<app> <h1>{ opts.title }</h1> <form onSubmit={ doCheck } > <input type="text" value=""> <input type="submit" value="チェック"> </form> <script> this.text = ""; doCheck(e) { // submit時の処理 } </script> </app>
イベントハンドラの指定には丸カッコ()
はいらない。また、イベントハンドラが呼ばれた後は自動でthis.update()
が呼ばれ、テンプレートが更新される。
他にはonKeyup
やonMouseover
などいろいろ試したのだが、onLoad
だと動作しなかった。おそらく不具合かな?
※追記
Riot.js公式ページのTag lifecycle、Listening to lifecycle eventsの項目を確認したところ、マウントされた後はupdate
イベントが発火する仕様だそうな。なので、イベントハンドラが呼ばれたら自動的にupdateされるので、onLoad
イベントは必要ない。またmount, update
イベントが発生した際に、this.on("mount", function(){})
やthis.on("update", function(){})
としてリスナーを登録できるとのこと。
css周り
- styleタグについて
htmlと同じように、tagファイル内でも<style>
は使用できる(ちなみに複数指定可)。tagファイル内に書くと、Riot.jsが自動で<head>
の最後に挿入してくれる。
※<script>
内では波括弧{}は評価されないので仕様不可。
<app> <!-- layout --> <h1>{ opts.title }</h1> <button onClick={ check }>チェック</button> <!-- css --> <style> li { list-style-type: none; } .done-true { color: gray; text-decoration: line-through; } </style> </app>
- class属性について
Riot.jsでは、その値が真になるプロパティ名は、クラス名のリストに追加される仕様となっている。例えば、
<p class={ foo: true, bar: 0, baz: new Date(), zorro: 'a value' }></p>
と書くと、<p class="foo bar zerro">
となる。
繰り返し(ループ)
html側のループはこんな感じ。tasks
という変数に配列が格納されていた場合の書き方。
body, done
というキーで値が取得できる。
<app> <!-- layout --> <ul> <li each={ tasks }>{ body }</li> </ul> <!-- javascript --> <script> this.tasks = [ {"body":"do this 1", "done": false}, {"body":"do this 2", "done": false}, {"body":"do this 3", "done": true}, {"body":"do this 4", "done": false}, ]; // 結果 // do this 1 // do this 2 // do this 3 // do this 4 </script> </app>
また以下のように、ループのインデックスを取得・表示することもできる。
<app> <!-- layout --> <ul> <li each={ task, key in tasks }> { key }: { task.body }</li> </ul> <!-- javascript --> <script> this.tasks = [ {"body":"do this 1", "done": false}, {"body":"do this 2", "done": false}, {"body":"do this 3", "done": true}, {"body":"do this 4", "done": false}, }; // 結果 // 0: do this 1 // 1: do this 2 // 2: do this 3 // 3: do this 4 </script> </app>
さらに連想配列の形式でのオブジェクト配列の場合。上記とほぼ記述の仕方は同じだが、微妙に違うのでこれは地雷かなと。
<app> <!-- layout --> <ul> <li each={ key, task in tasks }> { key }: { task.body }</li> </ul> <!-- javascript --> <script> this.tasks = { "a": {"body":"do this 1", "done": false}, "b": {"body":"do this 2", "done": false}, "c": {"body":"do this 3", "done": true}, "d": {"body":"do this 4", "done": false}, }; // 結果 // a: do this 1 // b: do this 2 // c: do this 3 // d: do this 4 </sciprt> </app>
ループのソースを確認したところ、以下のように書いてあった。(ver2.4.1
)
function each(els, fn) { var len = els ? els.length : 0 for (var i = 0, el; i < len; i++) { el = els[i] // return false -> current item was removed by fn during the loop if (el != null && fn(el, i) === false) i-- } return els }
つまりhtml側でノードについての処理はあるけど、JavaScript側で例えばAngular.jsならばangular.forEach()
のようなメソッドはなさそう。
条件分岐
条件分岐でも、true or false
ならば、htmlタグで記述できる。先ほどのonSubmit
の例をちょっと変えてみる。
<app> <!-- layout --> <h1>{ opts.title }</h1> <form onSubmit={ doCheck } > <input type="text" onkeyup={ input }> <input disabled={ !text } type="submit" value="チェック"> </form> <!-- javascript --> this.text = ""; input(e) { this.text = e.target.value; } doCheck() { // submit時の処理 } </app>
サブミット用のボタンのdisabled
属性はthis.text
の値が設定されている(true
)か否(false
)で分岐するが、
特にJavaScriptで記述することはない。
次はif
文の使い方。先ほどの例ではthis.text
の値によって、チェックボタンが押せたり押せなくなったりするが、
以下はボタンそのものが表示されたり、表示されなくなったりする。
<app> <!-- layout --> <h1>{ opts.title }</h1> <form onSubmit={ doCheck } > <input type="text" onkeyup={ input }> <div if={ text }> <input type="submit" value="チェック"> </div> </form> <!-- javascript --> this.text = ""; input(e) { this.text = e.target.value; } doCheck() { // submit時の処理 } </app>
ルーティング
ある意味でこれが一番の目玉かも。最近はやりのSPA
サイトを作るとしても、すごく簡単に書ける。
<app> <!-- layout --> <h1>{ opts.message }</h1> <ul> <li><a href="/">HOME</a></li> <li><a href="/page1">Page1</a></li> <li><a href="/page2">Page2</a></li> <li><a href="/page3">Page3</a></li> </ul> <route></route> <!-- javascript --> riot.route.start() riot.route.base('/') riot.route('/page*', function(tag) { riot.mount('route', 'page'+tag) }) riot.route('', function() { riot.mount('app',{ title: 'Routing demo', }) }) </app> <!-- other tags --> <page1> <h1>Page1</h1> </page1> <page2> <h1>Page2</h1> </page2> <page3> <h1>Page3</h1> </page3>
終わりに
まだまだ勉強中なのであくまで基本的なことしかやってない。 次は簡単だけどtodoリスト以外の何かを作ってみたい。やっぱり何かを実際に作った方がよっぽど力になる。(作るものは検討中)
では。