SPA でアプリケーションを作る際、angular1 の時はどうも「書かされている」感がして好きになれませんでした。x-tag を使って自前でカスタムエレメントを作っても良いのですが、それでも書かされている感がしますし、若干大げになりがちです。そんな中、angular 寄りで、かつ x-tag よりも薄い JavaScript ライブラリを tokuhirom がガシガシと作ってくれました。
GitHub - tokuhirom/sj.js
Tiny javascript view for custom elements based on incremental-dom. This library supports angular1 like templating.
https://github.com/tokuhirom/sj.js
x-tag の様に js 一つで動きます。そしてだいたい angular っぽく動きます。動作はコチラで確認して下さい。
<div sj-app="">
<p>Input something in the input box:</p>
<p>Name : <input type="text" sj-model="this.name" placeholder="Enter name here"></p>
<h1>Hello <span sj-bind="this.name"></span></h1>
</div>
まずこのソースの短さを見て下さい。コードは何も書いてません。機能的には angular のそれと同じですがモデル(sj-model)やバインド(sj-bind)を指定する際に this が必要という事が異なります。angular や riot.js の場合は独自に式をパースし実行ていますが、そこをスコープに依存した JavaScript 式で書ける様にしてあります(元々は式パーサも実装していましたがコードベースが肥大してきたのでこの方式に転換)。またテンプレートは angular の様に {{ name }}
という書き方にする事は出来ません。以下の様にノードを用意する必要があります。
<h1>Hello <span sj-bind="this.name"></span></h1>
当初はこの {{ name }}
というテンプレートを実装していましたが、サーバサイドテンプレートとの相性が良くない(間違って使った場合に意図しない eval 発火が起きる)のを理由に、バインド方式に変更されました。
イベントハンドラも一通り揃っています。
<script>
function CounterApp() {
this.counter = 0;
this.count = function() {
this.counter++;
this.update();
}
}
</script>
<div sj-app="CounterApp">
<button sj-click="this.count()">Click Me!</button>
<p sj-bind="this.counter"></p>
</div>
this は sj-app 属性を持ったのノードを指します。試しに動かしてみたい方はコチラで確認して下さい。
また sj-app にはコールバック関数名を指定できるので初期化を行う事もできます。自前でデータを更新したい場合は update() を呼び出します。もちろん repeat も動きます。その他のサンプルはココから参照して下さい。
また sj ではカスタムタグを生成する事も可能です。
customElements.define('sj-books', class extends sj.Element {
template() {
return `
<h3>Books</h3>
<input type="text" sj-model="this.filter" placeholder="検索するキーワードを入力して下さい" class="books-filter" />
<input type="button" sj-disabled="!!!this.filter" sj-click="this.clear()" value="クリア" />
<div class="books-container">
<div sj-repeat="x in this.books">
<div class="item" sj-if="this.matched(x,this.filter)" sj-click="this.clicked($index)">{{x.name}}</div>
</div>
</div>
`;
}
initialize() {
this.books = [];
this.clear = () => {
this.filter = '';
this.update();
};
this.clicked = (index) => {
const URI = 'http://www.amazon.co.jp/gp/search/';
location.href = URI + `?field-keywords=${encodeURIComponent(this.books[index].name)}`;
};
this.matched = (x,filter) => !!!filter || x.name.toLowerCase().indexOf(filter.toLowerCase()) != -1;
}
get books() {
return this.b;
}
set books(b) {
this.b = b;
this.update();
}
});
この例では書庫検索画面を sj-books という Web Component に仕上げています。動作を確認したい人はコチラで確認できます。コードを見て貰えると sj の薄さ、そして「書かされている感」がそれほどしない事が分かって頂けると思います。
angular 程色んな事が出来る訳ではありませんが、アプリケーションを作るには十分機能がそろったライブラリになっています。ライセンスは MIT です。