KnockoutでHandlebarsを使ってみる
これはKnockoutJSアドベントカレンダー22日目の記事です。
KnockoutJS Advent Calendar 2014 - Qiita
Knockoutでは外部のテンプレートエンジンを組み込んで使うことができます。
組み込み方はソースコード内に記載してあります。
knockout/templateEngine.js at master · knockout/knockout · GitHub
要約(ちょっと後半の訳があやしい)
// 独自のテンプレートエンジンを作りたい場合は次のようにしてください。 // // [1] ko.nativeTemplateEngineと同じようにこのクラスを継承します。 // [2] 'renderTemplateSource' をオーバーライドし、次のシグネチャのメソッドを実装します。 // // function (templateSource, bindingContext, options) { // // - templateSource.text() はテンプレートでレンダリングするテキストです // // - bindingContext.$data はテンプレートに渡すデータです // // - 他にも bindingContext.$parent, bindingContext.$parents, bindingContext.$root がテンプレートで使えます // // - options は "data-bind: { template: options }" で与えられたプロパティにアクセスすることができます // // - templateDocument はテンプレートの documentオブジェクトです // // // // Return value: DOM要素の配列 // } // // [3] 'createJavaScriptEvaluatorBlock' をオーバーライドし、 次のシグネチャのメソッドを実装します。 // // function (script) { // // Return value: Whatever syntax means "Evaluate the JavaScript statement 'script' and output the result" // // 'script' を評価して結果を出力します。 // // 例えば、 jquery.tmpl テンプレートエンジンは 'someScript' を '${someScript} に変換します // } // // This is only necessary if you want to allow data-bind attributes to reference arbitrary template variables. // これはあなたがデータバインドの属性を任意のテンプレート変数が参照する場合にのみ必要です。 // もしこれを許可したくない場合は 'allowTemplateRewriting' プロパティを false にします(ko.nativeTemplateEngineと同じように) // その場合は 'createJavaScriptEvaluatorBlock' をオーバーライドする必要はありません。
ko.templateSourcesあたりのソースコードに renderTemplateSource の引数の templateSource の詳しい内容が書かれています。
というわけで KnockoutJS から Handlebars を使ってみましょう。
公式ドキュメントの template バインディングの一番下にUnderscoreのサンプルがあるのでこれを真似てみます。
Knockout : The "template" binding
ソースはこんな感じ。
<div data-bind="foreach: people"> <div data-bind="template: { name: 'person-template' }"></div> <hr> </div> <script type="text/html" id="person-template"> <table> <tr> <th>Name</th> <td>{{name}}</td> </tr> <tr> <th>Gender</th> <td>{{gender}}</td> </tr> <tr> <th>Profile</th> <td> {{#each profiles}} <span>{{@index}} : {{{this}}}</span> {{/each}} </td> </tr> {{#if country}} <tr> <th>Country</th> <td>{{country}}</td> </tr> {{/if}} </table> </script> <script type="text/javascript"> ko.handlebarsTemplateEngine = function () { } ko.handlebarsTemplateEngine.prototype = ko.utils.extend(new ko.templateEngine(), { renderTemplateSource: function (templateSource, bindingContext, options) { var precompiled = templateSource['data']('precompiled'); if (!precompiled) { precompiled = Handlebars.compile(templateSource.text()); } var renderedMarkup = precompiled(bindingContext.$data).replace(/\s+/g, " "); return ko.utils.parseHtmlFragment(renderedMarkup); }, allowTemplateRewriting: false, createJavaScriptEvaluatorBlock: function(script) { return "{{" + script + "}}"; } }); ko.setTemplateEngine(new ko.handlebarsTemplateEngine()); function Person(name, gender, profiles, country){ this.name = name; this.gender = gender; this.profiles = profiles; if(country) { this.country = country; } } function ViewModel() { this.people = ko.observableArray([ new Person("Bob", "Male", ["Hello", "World"]), new Person("Mike", "Male", ["Foo"]), new Person("Katherine", "Female", []), new Person("Ken", "Male", [], "Japan"), ]); } ko.applyBindings(new ViewModel()); </script>
jsfiddleで見れます。
http://jsfiddle.net/vjj309sz/
明日は @motoyan_k さんの 「Knockout.hxについて」です。
よろしくおねがいします!