https://muut.com/riotjs/guide/#expressions
前回はJavaScript UIライブラリのRiotのカスタムタグについて、その全体像を把握しました。今回はカスタムタグ内で使用できるHTML Expressionsを見ながら、どのようにカスタムタグをつくるか見ていきましょう。
##Expressions
カスタムタグは{ }
で囲むことでHTMLに式(Expressions)を使用できます。下記例ではh3タグのテキストをopts.titleの値に置き換えています。
・・・
<h3>{ opts.title }</h3>
・・・
また以下のように属性に使用することも可能です。
<h3 id={ /* attribute_expression */ }>
{ /* nested_expression */ }
</h3>
式は以下のようにどんなJavaScriptも記述できます。
{ title || 'Untitled' }
{ results ? 'ready' : 'loading' }
{ new Date() }
{ message.length > 140 && 'Message is too long' }
{ Math.round(rating) }
しかし、HTML式の目標はコードをクリーンで簡潔に保つことです。式が複雑になるようならupdateイベントリスナーで値を生成し、その値を参照することをお勧めします。
<my-tag>
<!-- the `val` is calculated below .. -->
<p>{ val }</p>
// ..on every update
this.on('update', function() {
this.val = some / complex * expression ^ here
})
</my-tag>
##Boolean attributes
checked, selectedなどの真理値フィールドは式の結果によって、付けたり付けなかったりできます。
<input checked={ null }>
は<input>
になりcheckedは付けられません。しかし、<input checked={ true }>
なら<input checked>
になります。
・・・
<input type="checkbox" checked={ done } ・・・>
・・・
##Class shorthand
RiotはCSSクラス用に特別なシンタックスを用意しています。
<p class={ foo: true, bar: 0, baz: new Date(), zorro: 'a value' }></p>
上記のclass定義はclass="foo baz zorro"
になり、真理値的にfalseなbarは取り除かれます。
同様に以下の例もdoneが真の場合のみ、completedクラスが設定されます。またこの記法はclass定義以外でも使用できます。
・・・
<label class={ completed: done }>
・・・
##Printing brackets
{
,}
は\でエスケープできます
##Customizing brackets
式の{ }
はカスタマイズ可能です。
riot.settings.brackets = '${ }'
riot.settings.brackets = '{{ }}'
##Etc
Styleタグ内の式は無視されます
##Render unescaped HTML
式がタグを持つ場合、自動的にエスケープして表示されます。もしタグをエスケープせずに出力する必要がある場合、以下のようにします。ただし、XSS攻撃に気を付けて使用してください。
<raw>
<span></span>
this.root.innerHTML = opts.content
</raw>
<test>
<p><raw content={opts.title}</p>
</test>
<script>
riot.mount('test',{title: "<strong>RAW</strong> value"})
</script>
##Nested tags
エスケープの項で、すでに使用してますが、カスタムタグ内のレイアウトで別のカスタムタグを使用できます。
また、前回、Mountingの項でオプションの話をしましたが、子カスタムタグのオプションはタグの属性として渡します。以下の例では子タグのopts.titleに文字列"Get started"を渡しています。
<test>
<p><raw content={opts.title}</p>
</test>
<todo>
・・・
<test title="Get started"></test>
・・・
<todo/>
##Nested HTML
以下のように、カスタムタグの内側にHTMLをネストすることができます。
<body>
<my-tag>
<h3>Hello world!</h3>
</my-tag>
</body>
この場合、カスタムタグはネストされたHTMLを以下の形で利用できます。<inner-html>
タグは親タグの子要素を取得し、自身の子に移動させるスクリプトです。これにより任意の位置にネストされたHTMLを表示できます。
<inner-html>
var p = this.parent.root
while (p.firstChild) this.root.appendChild(p.firstChild)
</inner-html>
<my-tag>
<p>Some tag specific markup</p>
<!-- here comes the inner HTML defined on the page -->
<inner-html/>
</my-tag>
##Named elements
id、name属性をもつエレメントは自動的にthisのフィールドになります。
<login>
<form id="login" onsubmit={ submit }>
<input name="username">
<input name="password">
<button name="submit">
</form>
// grab above HTML elements
var form = this.login,
username = this.username.value,
password = this.password.value,
button = this.submit
</login>
また<div>{ username.value }</div>
のようにHTML式からもアクセスできます。
##Event handlers
イベントハンドラの定義は以下のようにします。ハンドラが呼ばれると自動的にthis.updateが実行され、画面が再描画されます。
<login>
<form onsubmit={ submit }>
</form>
// this method is called when above form is submitted
submit(e) {
}
</login>
チェックボックスとラジオボタンを除き、デフォルトのイベントは自動的にキャンセルされます。機能的にはe.preventDefault()が既に呼ばれた状態になっています。つまりsubmitを押してもデータの送信は発生しません。これを有効にするには次のようにします。
submit() {
return true
}
##Conditionals
表示非表示の切り替えを以下のように条件節を使用することで実現できます。
<div if={ is_premium }>
<p>This is for premium users only</p>
</div>
また条件節には以下の3パターンがあります。
- show: trueのとき
style="display: ''"
を用いエレメントを表示します - hide: trueのとき
style="display: 'none'"
を用いエレメントを非表示にします - if: falseの時、エレメントを追加しません。
#Loops
ループにはeach
節を用います。
・・・
<ul>
<li each={ items }>
<label class={ completed: done }>
<input type="checkbox" checked={ done } onclick={ parent.toggle }> { title }
</label>
</li>
</ul>
・・・
eachはArrayの要素を繰り返すことができます。例ではitemsの要素を繰り返しています。またitemesの要素がpush(), slice(), splice()メソッドを用いて増減した場合、自動的に表示は更新されます。
eachを用いた場合、ループ中、コンテキスト(this)がその要素に置き換わります。もともとのthisを参照する場合、parent
を用います。例ではループ中の要素が、親要素のparent.toggle
をonclickイベントハンドラとして設定しています。
またイベントハンドラはループ中の要素にe.itemでアクセスできます。
・・・
toggle(e) {
var item = e.item
item.done = !item.done
return true
}
・・・
なお、ループ中のコンテキストは正確にはタグインスタンスです。もともとのデータには手を加えていません。
またリテラル値のArrayの場合、次のようにします。
<my-tag>
<p each="{ val, i in arr }">{ i }: { val }</p>
this.arr = [ true, 110, Math.random(), 'fourth']
</my-tag>
さらに、パフォーマンス上推奨されませんが、オブジェクト(ハッシュ)もループできます。
<my-tag>
<p each="{ name, value in obj }">{ name } = { value }</p>
this.obj = {
key1: 'value1',
key2: 1110.8900,
key3: Math.random()
}
</my-tag>
以上です。Riotのカスタムタグでの条件節やループ、イベントハンドリング、HTML式について見てきました。
前回載せたサンプルコードは読める程度にはなったかと思います。
できることを見ていく形になったので、したいことをどうやるかの観点からGet-Startedを作りたいところですね。
作ってみました。自作のGetting Started