素のHTMLでは、もちろんできないんですが、仮想DOM系のライブラリ、
- React
- Polymer
- Riot.js
あたりを使うとできます。設計上ありかという話は置いておいて、便利です。Reactだと何を今更というかJavaScriptなのでやり放題。Polymerもシリアライズが途中に入るもののOKです。
HTML慣れしているほど案外盲点な気がするので、Riotの例を中心に書き留めておきたいと思います。
複数の属性を渡すとき
HTMLで慣れた作法に従うと、例えば、
<path
file-name={ file.name }
file-ext={ file.ext }
file-dir={ file.dir }
file-icon={ file.icon }>
</path>
みたいにして複数の属性を渡すことを考えると思います。ただ、属性値をひとつずつ分解して渡すのはかなり面倒ですし、JavaScript側で渡すものを増やしたい場合に、HTMLを必ずいじる必要がありその点も面倒です。
- 面倒1: オブジェクトを分解して渡すのが手間
- 面倒2: 仕様を変えると必ずHTML変更の必要あり
ここで、もし文字列以外も渡せるとしたら? こう書けます。
<path file={ file }></path>
シンプルですね。あとは、コンポーネント(仮想DOM)内から、オブジェクトにそのままアクセスできるので、好きにすればOKです。(必要があれば、渡された内容のバリデーションは、コンポーネントに任されます)
配列を属性として渡す
複数の子要素を持つコンポーネントはしばしば必要になります。たとえば、こんな構造のコンポーネントがあったとします。
<member-list>
<programer name="John"></programer>
<programer name="Sean"></programer>
<designer name="Becky"></designer>
<designer name="Taro"></designer>
</member-list>
もし、配列が渡せないとすると、member-list
の内側に要素を手動で書いて個別に渡すか、なんらかの情報ソースのidなりuriを渡して、コンポーネント内と通信する必要が出てきます。前者は、member-list
のカプセル化が不完全になるので不適切なケースが多く、後者は必要なことはあるけど面倒です。なので、結局は配列かオブジェクトを渡す必要が出てきます。
※要素数は可変なので、一人ずつ属性をつくって渡すのはナシです、念のため。
<member-list members={ members }></member-list>
<script>
var this.members = [
{ name: 'John' },
{ name: 'Sean' },
{ name: 'Becky' },
{ name: 'Taro' }
]
</script>
こう書けると、ほっとするほど自然ですね。
属性として関数を渡す
ReactやRiotの革命的に便利だったのは、この点です(大げさかも)。見た目上は、HTMLにインラインスクリプトとして、
<button onlick="alerat(message)">Click me!</button>
とか書いているように見せかけて、その実、DOMのイベントハンドラに登録するというアレです。これのおかげで、
- ある人は「jQueryは死んだ」
- ある人は「コード量が一桁減った」
と言ったとか言わないとか。
独自タグ(コンポーネント)に属性を渡す場合は、イベントハンドラというよりは、コールバックに近いイメージかもしれません。たとえば、削除ロジックを子要素ではなく親要素に持たせるケースなど。
<task-list>
<task-item remove={ remove }></task-item>
<task-item remove={ remove }></task-item>
</task-list>
<task-item>
<span>{ task }</span>
<button onclick={ removeMe }> X </button>
<script>
removeMe (e) {
opts.remove()
}
</script>
</task-item>
いくつかの例外
Riot.js特有の話になります。必ず属性にng-
をつけたAngularJSと異なり、Riot.jsでは、属性はHTMLのもともとの属性を使います。それだと支障のある要素については、コンパイルの段階で別名に置き換えるという手をとっています。
イメージタグ
Riot.jsの実装だと、一時的にテンプレートがDOMに出力されます。存在しないアドレス({ url }
)にリクエストが行ってしまうとまずいので、コンパイルの時点で一時的にriot-src
に書き換えられます。
- TAGファイル:
<img src={ url }>
- コンパイル後:
<img riot-src={ url }>
- DOM(評価後):
<img src="path/to/somewhere">
インラインスタイル
これも同様の理由で、riot-style
に置き換えられます。
- TAGファイル:
<div style={ url }>
- コンパイル後:
<div riot-style={ url }>
- DOM(評価後):
<div style="color: red">
そのほか
2.0.12以降、d
もriot-d
になります。SVGで、<svg d="hoge">
で使うため。(他にももう少し増えそうな予感)
まとめ
るほどのことはありません。最後に、むしろこれが書きたかった「属性変換の一覧」を載せておきます。Riot.jsを深く知りたい方へ。(といってもコード短いので、安心してどっぷり行きましょう。底は浅いです)
付録: Riot.jsの属性変換一覧表
タイプ | .tag |
コンパイル後 | DOM(評価後) | DOMの値の例 |
---|---|---|---|---|
画像Url | src |
riot-src |
src |
'images/logo.svg' |
インラインスタイル | style |
riot-style |
style |
'color:red' |
真偽値属性 |
checked disabled selected ... |
__checked __disabled __selected ... |
checked disabled selected ... |
'checked' 'disabled' 'selected' ... |
String | foo |
foo |
foo |
'bar' |
Bool | foo |
foo |
foo |
'true' か 'false' ※Stringに変換 |
Function | foo |
foo |
イベントハンドラ | 関数 |
Object/Array | foo |
foo |
n/a | ※削除される |
※いずれの場合も、独自タグ(コンポーネント)であれば、その内部からopts.srcやらopts.style、opts.fooなどで元々の値を参照できます。