宣言型model-viewクライアントフレームワーク「knockoutjs」を試す

knockoutjs (http://knockoutjs.com/)というクライアントフレームワークを試してみた。モデルとUIの依存関係を宣言することにより、自動的に更新してくれるというものだ。売り文句をまとめると: OSS, 他フレームワークとの利用可能、軽い、ブラウザサポート等々。誰が管理しているのか知らないが、ドキュメントはよく書かれていてサポート体制はしっかりしているよう。

必要なもの


<script type='text/javascript' src='./jquery-1.4.3.js'>
<script type='text/javascript' src='./knockout-1.1.0.debug.js'>
インストール詳細はこちら: http://knockoutjs.com/documentation/installation.html

debug.jsは開発用なんで、本番にはそれ用のをダウンロードする。

モデル属性の表示


<span data-bind="text: personName">
これだけで、モデルのpersonNameという属性が表示される。「data-bind」ってのはHTMLエレメントをモデルのデータと結び付けるKO特有の属性。値は name1: value1, name2: value2とJSONのように記述する。

モデル属性の更新


<input data-bind="value: personName" />
これで、入力された値がモデルの属性に書き込まれる。ディフォルトではフォーカスが外れるタイミングで更新される。

モデル属性の更新: リアルタイム

<span data-bind="text: personAge">

<input data-bind='value: personAge, valueUpdate: "afterkeydown"' size='5'/>

このvalueUpdateafterkeydownにするとキーストローク毎にリアルタイムに更新されるようになる。

モデルの定義


var myViewModel = {
personName: ko.observable('Bob'),
personAge: ko.observable(23),
}
personNameはko.observableという、値を管理するクラスのインスタンスを'Bob'という初期化したもの、と考えればいいんだろう。「observable」ってのは「値の更新を監視する対象物」という意味。koが値の変化を認識して、依存しているものをに自動的に更新してくれる。このobservableがKOのミソと言えるだろう。

dependentObservable=他のフィールドに依存するモデルフィールド


myViewModel.summary=ko.dependentObservable(
function() {
return this.personName() + " (" + this.personAge() + ")";
},
myViewModel
);
他のモデル属性から計算されるフィールドの定義。本来ならpersonNameと同様に{ summary:.. }としたいところだが、javascriptのオブジェクトリテラルでは上の値を参照できないので、別に定義しなくてはならない。(こういうときpythonのlocals()は便利だと思う)
myViewModelのメソッドを定義しているような感じだが、myViewModelを明示的に渡さなければいけない。

任意の関数を呼出す


myViewModel.testHandler=function() {alert('hi')};
モデルに任意の関数を定義してこのようにバインドすることができる

<button data-bind='click: testHandler'/>

最後に


ko.applyBindings(myViewModel);
ごれが以上の依存関係の宣言を有効にする。

IEがまたもやネックに…

ko.observableのシンタクスについて:作者は普通の属性のように代入表現(personName="Alice")により値を更新させるのを可能にしたかったようだが、IEがそうさせなかったようだ。なので、javaなどのアクセサのように、personName()で読みpersonName(newValue)で書き込みをするようになっている。

まとめ

依存は最新のjQueryだけ。スクリプト自体も確かに小さい。hello worldを作った限では使い易い。第一印象はいい。問題は本物のプロジェクトに投入したときに、プラスになるかお荷物になるかだ。これはやってみなければわからない。

上記の例以外に、リスト管理とテンプレートとモデルの対応付けもチュートリアルにあったので、紹介していきたい。

実例

これをコピペして保存すればそのまま動くハズ。ご本家サイトにも色々実例がある: http://knockoutjs.com/


<head>
<!-- knockoutjs フレームワークのhelloworld http://knockoutjs.com/ -->
<script type='text/javascript' src='./jquery-1.4.3.js'>
<script type='text/javascript' src='./knockout-1.1.0.debug.js'>
</head>
<body>
<div id="top">
<p>
お名前:
<!-- data-bindってのはHTMLエレメントをモデルのデータと結び付けるKO特有の属性。
値は name1: value1, name2: value2とJSONのように記述する。
-->
<span data-bind="text: personName">
<!-- 下に定義されたmyViewModelのフィールドの値を表示する -->
<input data-bind="value: personName" />
<!-- 入力された値をモデルのpersonNameに書き込む。
ディフォルトではフォーカスが外れるタイミングで更新される。
-->
</p>
<p>
年齢:
<span data-bind="text: personAge">
<!-- 上と同じく表示フィールドをモデルに対応させる。 -->
<input data-bind='value: personAge, valueUpdate: "afterkeydown"' size='5'/>
<!-- このvalueUpdateの値によりキーストローク毎にリアルタイムに更新されるようになる。 -->
</p>
<p>
bio:
<span data-bind="text: summary">
<!-- 同上 -->
</p>
</div>

<script type='text/javascript'>
// モデルの定義
// ko.observableという値を管理するクラスを初期化していると考えればいいのだろう。
// 「observable」ってのは「値の更新を監視する対象物」という意味。
// koが値の変化を認識して、依存しているものをに自動的に更新してくれる。
var myViewModel = {
personName: ko.observable('Bob'),
personAge: ko.observable(23),
// 任意の関数を呼出すことも可能。
//