knockoutjsメモ

チュートリアルレベルをさらっと触ってみた感想

案外簡単に利用できるように感じで動的な画面が必要なケースには本当に強力な仕組み。ただ、ASP.NETとの統合利用するためには、ページのGET、POSTではなくAJAX的な実装にする必要があって、HTML中心のWebアプリの延長ではない点を考えないといけない。

knockoutjsが提供する主な機能には以下のようなものがある

  • データ依存追跡
    • データモデルの変更を自動的にUIに反映できる機能
  • 宣言的バインディング
    • UIパーツとデータモデルを連携させる機能
  • テンプレート
    • 動的にUIを生成できる機能
  • バインディング拡張
    • バインド機能を独自拡張できる機能

データ依存追跡

WPF/SilverlightのOnPropertyChanged機能相当で、ViewModelオブジェクトのメンバーをko.observableオブジェクトとして宣言することで変更をHTML要素に通知することができるようになる。

    var viewModel = {
        lastName: ko.observable("山田"),
        firstName: ko.observable("太郎"),

コレクション用には ko.observableArrayが用意されている

    var viewModel = {
       availableRegions : ko.observableArray([
            new region("北地区", 1), new region("南地区", 2),
            new region("東地区", 3), new region("西地区", 4) ]),

他の項目から計算される項目には ko.dependentObservableが用意されている。

    viewModel.fullName = ko.dependentObservable(function () {
        return this.lastName() + " " +this.firstName();
    }, viewModel);

宣言的バインディング

ViewModelオブジェクトのメンバーをHTML要素の各種項目に関連づけるWPF/SilverlightXAMLでのデータバインディング相当の機能。data-bind属性を指定する

表示内容(innerText)をバインドする場合はtextバインディングを利用する。htmlやcss、style、attrのバインドも用意されている

<span data-bind="text: fullName"></span>

テキスト入力をバインドする場合にはvalueバインディングを利用する。

<input data-bind="value: firstName, valueUpdate: 'afterkeydown'"/>

valueUpdateを利用して変更を検知するタイミングを調整できる。change(既定の動作で他のコントロールに移動したタイミング、Selectについては直ぐ)、keyup, keypress, afterkeydownがある。

checked バインディングチェックボックスラジオボタンで利用でき、optionsバインディングはドロップダウンリストの一覧をバインドするためのものなどもある。コントロールの有効無効を制御したい場合はenableやdisbaleバインディングも利用できる

携帯有り:<input type='checkbox' data-bind="checked: hasCellphone" />
携帯電話:<input type='text' data-bind="value: cellphoneNumber, enable: hasCellphone" />

click時の実行処理を関連づける場合にはclickバインディングを利用する。個別eventやsubmit用のものもある

<button data-bind="click: confirmRegion">選択地区確認</button>

テンプレート

テンプレートエンジンはjquery.tmplを利用している。テンプレートをネストして利用できる。foreachオプションを利用すると追加、削除された場合に全ての要素の再描画しなくて済む。

<div data-bind='template: { name : "regionTemplate", foreach: availableRegions }'> </div>
<script id='regionTemplate' type='text/html'>
<span>${ regionName }(${ regionCode }) </span> 
</script>

バインディング拡張

グリッド表示などカスタムコンポーネントを作成する場合に威力を発揮しそう。

<div data-bind="simpleGrid: gridViewModel"> </div>

サンプルコード

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Home</title>
    <link href="/Content/Site.css" rel="stylesheet" type="text/css" />
    <script src="/Scripts/jquery-1.5.1.min.js" type="text/javascript"></script>
    <script src="/Scripts/modernizr-1.7.min.js" type="text/javascript"></script>
    <script src="/Scripts/jquery.tmpl.js" type="text/javascript"></script>
    <script src="/Scripts/knockout-1.2.1.js" type="text/javascript"></script>
</head>
<body>    
<h2>knockout.jsサンプル</h2>
<p>バインドのさまざまな例</p>
<div>
<p>姓:<input data-bind="value: lastName, valueUpdate: 'afterkeydown'"/></p>
<p>名:<input data-bind="value: firstName, valueUpdate: 'afterkeydown'"/></p>
<p>氏名:<span data-bind="text: fullName"></span></p>
<p>携帯有り:<input type='checkbox' data-bind="checked: hasCellphone" /></p>
<p>携帯電話:<input type='text' data-bind="value: cellphoneNumber, enable: hasCellphone" /></p>
<p>地区:<select data-bind="options: availableRegions, optionsText: 'regionName', value: selectedRegion, optionsCaption: '選択してください'"></select></p>
<p>地区コード:<span data-bind="text: selectedRegion() ? selectedRegion().regionCode : '未選択'"></span></p>
<p><button data-bind="click: confirmRegion">選択地区確認</button></p>
</div>

<p>地区一覧(テンプレート例)</p>
<div data-bind='template: { name : "regionTemplate", foreach: availableRegions }'> </div>
<script id='regionTemplate' type='text/html'>
<span>${ regionName }(${ regionCode }) </span> 
</script>

<script type="text/javascript">
    var region = function(name, code) {
        this.regionName = name;
        this.regionCode = code; 
    }; 
    var viewModel = {
        //項目の定義
        lastName: ko.observable("山田"),
        firstName: ko.observable("太郎"),
        hasCellphone: ko.observable(false),
        cellphoneNumber: ko.observable(""),
        availableRegions : ko.observableArray([
            new region("北地区", 1),
            new region("南地区", 2),
            new region("東地区", 3),
            new region("西地区", 4) ]),
        selectedRegion: ko.observable()
    };
    //他項目に依存するプロパティの定義
    viewModel.fullName = ko.dependentObservable(function () {
        return this.lastName() + " " +this.firstName();
    }, viewModel);
    viewModel.selectedRegionName = ko.dependentObservable(function () {
        return this.selectedRegion() ? this.selectedRegion().regionName : "未選択";
    }, viewModel);

    //ボタン押下時のアクション
    viewModel.confirmRegion = function () {
        confirm("現在選択されている地区は" + this.selectedRegionName());
    };

    //ViewとViewModelをバインドする
    ko.applyBindings(viewModel);
</script>
</body>
</html>