seconds3.ViewModelを作るModelの次はViewModelを作っていきます。ViewModelは「UI独自の状態や処理を管理するオブジェクト」です。Modelをラップする形で、ModelとViewの仲立ちをします。Todoリストであれば、以下のような処理がViewModelの担当になります。最終更新日時をUIで表示する文字列に変換する。Todoの追加ボタンや、完了したTodoを消すボタン、などの処理を実行するメソッドを提供する。完了したTodoを消すボタンの利用可/不可状態を制御する。(完了したTodoが1つもなければ利用不可)Todoを追加する際にタイトルを設定する口を提供する。UI依存のロジックをViewModelとして分離することで、Modelはより純粋なビジネスロジックだけを提供する層にできます。UIの変更があってもViewModelだけでの変更で済み、Modelには波及しにくくなります。「UIを抽象化してオブジェクトにしたもの」考えるとわかりやすいかも。イメージ的には以下のような感じ。実際のUI操作シナリオに沿った形でテストケースが書けるのもポイントです。作成するViewModelは以下の3つです。TodoListViewTodoの一覧部分に対応するViewModelです。TodoViewTodo一覧の中の各Todoに対応するViewModelです。TodoInputForm新しいTodoを作成する入力フォーム部分に対応するViewModelです。ViewModelではKnockoutの機能を利用します。Todoの一覧を持ちます。itemsはモデルの変更通知を受けて随時更新。UI上のボタンを押下した際の処理をメソッドで提供します。完了したTodoの削除ボタンが利用可かどうかを管理します。Object.freeze(TodoListView);});TodoView一覧の各Todoに対応するViewModelです。タイトル(title)や最終更新時刻(lastModified)などを持ちます。Todoの完了操作や、(個別の)削除はTodoごとの操作なので、こちらに定義します。Object.freeze(TodoView);});TodoInputForm最後はTodoInputForm。Todoの新規作成フォーム部分に対応するViewModelです。タイトル(title)を受け取る口とエラー情報(error)を持ちます。Todo追加時にタイトルが未入力だったりした場合、エラーメッセージがerrorに入りデータバインディングでUIに表示される仕組み。Todo追加の本処理もこちらに定義しています。Object.freeze(TodoInputForm);});ViewModelとModelの生成と関連付けはContainerJSで管理します。ContainerJS用のモジュール定義(composing/modules.js)も用意しておきます。modules;});4.ViewModelをテストするViewModelもテストしていきます。今回はModelのモックは用意せず、ViewModel+Modelの動きをまとめてテストするようにしました。TimeSourceのみmockを使います。テストでもViewModel,Modelの作成はContainerJSを使って行います。specTodoの追加・削除が動作することを確認。追加・削除に応じて一覧のデータが増減することもチェック。specTodoモデルのプロパティが正しく反映されていることを確認。specタイトルを入れてTodoを追加できる、タイトルが未入力だと追加できずエラーが表示される、といったあたりを確認。});5.View(HTML/CSS)を作るModelとViewModelができたら、Viewを作成して、View/ViewModel/Modelを生成する部分のコードを書いていきます。「data-bind」でViewとViewModelを繋いでいきます。ここにViewModelの○○で保持されるデータを流し込むこのボタンが押されたら、ViewModelの▲▲メソッドを実行、といった感じ。#ddd;}最後はmain.js。main.jsはhtmlで最初に読まれるスクリプトで、以下の処理を行います。DIコンテナを使ってViewModel/Modelを作るKnockoutのAPIを呼び出して、ViewModelとViewの関連付け(データバインド)を実行します。});必要なソースの作成はこれで完了。動いているところはこちらでみられます。結合してリリースするAntタスクを定義していきます。結合はRequire.jsからリリースされているr.jsを使用。Require.jsの依存関係をたどって必要なファイルを集め、1ファイルに統合します。closure等を使用してminifyを行う機能もあります。サンプルでは、main.jsにすべてのファイルを統合する形にしています。リリース版ではmain.jsが統合版に置き換わり、require.jsとmain.jsのみの読み込みとなります。最初に読むファイルは変わらないので、HTMLの変更は不要。