はじめまして!
スマホ対応アメーバピグ Webアプリ版の開発を担当している吉川浩太と申します。

今回の記事では、さいきん巷であまり話題になっていないjavaScriptのMVVMフレームワーク「knockout.js」の機能と特徴を、簡単にご紹介できればと思います。

knockout.js?

knockout.js (http://knockoutjs.com/)

knockout.jsはMVVM(Model-View-ViewModel)パターンのフレームワークです。
双方向データバインディングやアイテムテンプレート等の機能があり、SilverlightやWPF開発者にはかなりとっつきやすいフレームワークだと思います。

WebアプリではDOMを動的にゴリゴリ更新/生成することがほぼ必須になると思いますが、knockout.jsではDOMを直接操作することが(ほぼ)無くなります。
データのやり取りだけを指示しUIの更新はknockout.jsに任せるといった形になりHTMLとロジックと切り分けて開発が可能になるハズです。

名前はどんなwebアプリも簡単にノックアウト出来るよ!って意味に違いないです。


導入方法

knockout.jsは単体で動作する素敵なフレームワークです。
特にライブラリとぶつかる事はないのでjQueryと併用すると使いやすいと思います。
※執筆時点での最新版はv.2.1.0ですので、こちらを使用して進めて行きます。

<script type="text/javascript" src="./js/knockout-2.1.0.js"></script>
<script type="text/javascript" src="./js/jquery-1.7.2.min.js"></script>


最初の一歩

簡単な例を用いてknockout.jsの素敵さを説明します。

HTML:

<p><input type="text" data-bind="value: message, valueUpdate: 'afterkeydown'"></p>
<p id="result" data-bind="text: message">ここに入力されたテキストと同じものが入る</p>


javaScript:

$(function(){
    //ViewModelを定義
    function TestappViewModel(){
        var self = this;
        self.message = ko.observable(''); //初期値は空文字列を指定
    }
    //bindingスタート
    ko.applyBindings( new TestappViewModel() );
});



実行結果:

入力されたテキストが、そのまま下に表示されるだけのコンテンツです。
ここで注目して欲しいのは、ソース上でDOM操作は一切行なっていないということです。
messageは常に双方向で監視されているため、値をHTML上で変更すれば、自動的にViewModelのmessageプロパティは更新されます。
その結果、text(挿入テキスト)にmessageを指定している#resultの表示も変わるというワケなのです。
バインディング凄い!(迫真)


基本的なお作法

上の例からknockout.jsの基本的なお作法を説明したいと思います。

バインディングの紐付け
HTML要素にdata-bind属性を指定することにより、ViewとViewModelとの紐付けを行います。
data-bind属性の値にはバインディング内容と、それに紐付くプロパティをJSONのような形で指定します。

<input type="text" data-bind="value: message, valueUpdate: 'afterkeydown'">



バインディングプロパティの設定
ViewModelのプロパティにko.observableメソッドを指定することでバインディング対象とみなし、常に値を監視します。

self.message = ko.observable('');


また、例では使用しませんでしたが複数データをバインディングする場合はko.observableArrayが使用できます。

self.messageList = ko.observableArray();


バインディングの開始
最後にko.applyBindingsで対象のViewModelを渡せばView-ViewModel間でバインディングが開始されます。

ko.applyBindings( new TestappViewModel() );


値の取得・設定
バインディング要素の値を取得・設定するときは関数呼び出しになります。
上の例では下記のようになります

・値の取得

self.message();


・値の設定

self.message( 'huge' );


うっかり

self.message='hoge';


と、やりたくなりますが、そこは違いのわかる大人の対応をしましょう。



色々なバインディング

上の例でさりげにvalue、textといったバインディングを行なっていますが
knockout.jsには標準で様々なバインディングが用意されています。
公式のDocumantation

ここで各々の説明は省きますが大きく分けて3つの種類にカテゴライズされています
・見た目をバインディング(text、visible等)
・繰り返し処理、分岐処理をバインディング(foreach、if等)
・押下、入力などの操作・入力処理をバインディング(click、checked等)

また標準で用意されていないバインディングはカスタムバインディング作成することが出来ます。


実例

いくつかのバインディングを用い、ごくごくシンプルなTODOアプリを作ってみます。

HTML:

<p>
    <input type="text" value="" placeholder="TODOを入力" data-bind="value: todoInputValue"> <button data-bind="click:addTodo">追加</button>
</p>

<ul data-bind="foreach: todoList, visible:todoList().length>0">
    <li><span data-bind="text:todoText">Todo</span> - <button data-bind="click:$root.removeTodo">削除</button></li>
</ul>


javaScript:

$(function(){

    //Modelを定義
    function TodoModel(value){
        var self = this;
        self.todoText = ko.observable(value);
    }

    //ViewModelを定義
    function TestappViewModel(){
        var self = this;

        self.todoList = ko.observableArray(); //todoリスト
        self.todoInputValue = ko.observable(''); //追加todoテキスト

        //追加
        self.addTodo = function(obj, e)
        {
            if( !self.todoInputValue() )return; //空文字の場合は処理しない
       
            //入力されたテキストでTodoModelを作りTodoリストに挿入
            self.todoList.unshift( new TodoModel( self.todoInputValue() ) );
            self.todoInputValue('');
        };

        //削除
        self.removeTodo = function(obj, e)
        {
            //イベントバインディングで実行される関数の第1引数にはModel、第2引数にはイベントオブジェクトが渡される
            self.todoList.remove(obj);
        };
    }
    //bindingスタート
    ko.applyBindings( new TestappViewModel() );
});


実行結果:

見た目はだいぶ残念な感じは否めませんが、最低限それっぽくはなっていると思います。

上の例を説明します。

ViewModelにTestappViewModelを定義し
・Todoリスト
・追加する文言
・追加処理
・削除処理
を管理、
そしてModelにTodoModelを定義し
・表示する文言
を管理しています。

実際の表示の紐付けはView(HTML)で行います。

「追加」ボタンの押下で、addTodoメソッドを呼んでいます。
viewModel内の関数を呼ぶには値に関数名を指定します。


<button data-bind="click:addTodo">追加</button>


リストはforeachバインディングで、その数だけ自動で要素が作られます。
また、件数が0の場合は非表示にしています。

<ul data-bind="foreach: todoList, visible:todoList().length>0">…</ul>


追加されたTodoの削除ボタンはModelからViewModelのremoveTodoメソッドを呼びます。
その場合は$root.を頭に付けることでrootのViewModelにアクセス出来ます。

<button data-bind="click:$root.removeTodo">削除</button>


この例でも処理として行なっているのはTODOリストにデータを追加/削除しているだけでDOM操作は直接していません。
数あるバインディングの中でもforeachバインディングは非常に強力で色々な応用が効くのではないかと!
knockout.js凄い!!(迫真)



おわりに

「knockout.jsでさくさくWebアプリ開発」は以上となります。
ほんの一部の機能をかなりざっくりの説明しかできませんでしたが、この記事で1人でも多くの方がknockout.jsに興味を持ち、触ってもらえれば幸いです。
結果、日本語の情報が充実すればと!

それでは皆様、今後もエレガントな開発ライフを!!