Knockout.js 日本語ドキュメント

Observable (ViewModelをつくる)

Knockout は次の3つの思想に基づいています。

  1. Observable と依存関係トラッキング
  2. 宣言型バインディング
  3. UIテンプレート

ここでは上記3つのうち最初の1つを紹介しますが、その前に、MVVM パターンと View Model のコンセプトについて考えてみましょう。

MVVM と View Model

Model-View-ViewModel (MVVM) はユーザインターフェイスを構築するための設計パターンであり、概念モデルです。 MVVM では、プログラムを次の3つに分割して設計することで、機能的なUIのコードをシンプルに保ちます。

  • Model:  いかなるUIにも依存しない、ビジネスドメインのデータと操作を表すオブジェクトです。
    Knockout を使う場合、サーバに保管されたデータを取得・変更するために、サーバサイドコードをAjaxで呼び出すことになるでしょう。

  • ViewModel:  UIで必要とされるデータと操作を表現する、純粋なオブジェクトです。 例えば、アイテムを追加・削除できるリストを実装するとき、ViewModel では データ「アイテムのリスト」 操作「追加」「削除」 を公開します。

    ViewModel自体 は UI ではないことに注意してください。ボタンや表示スタイルに関するいかなる情報も含みません。 さらに ViewModel は永続データモデルではありません。ユーザが画面を操作する上で、保存されていない(あくまでメモリ上の)データを保持します。 Knockout を使えば、ViewModel はいかなるHTMLに関する情報を含まない、純粋な JavaScript オブジェクトとなります。 このように ViewModel を抽象的に保つことで、簡潔さを損なうこと無く改良を加えていくことができます。

  • View:  ViewModel の状態に応じてインタラクティブに変化する UI です。 ViewModel が提供する情報を表示し、ViewModel にコマンドを送ります。(ユーザがボタンをクリックした時など) そして ViewModel に変化があれば更新します。

    Knockout を使う場合、バインディングで ViewModel とリンクしたシンプルなHTMLドキュメントが View となります。 あるいは、テンプレートを使って ViewModel のデータから HTML を生成することができます。

Knockout で ViewModel を作るには、次のように JavaScript オブジェクトを定義するだけです。

var myViewModel = {
	personName: 'ボブ',
	personAge: 123
};

その後、宣言型バインディングを使用して、この ViewModel の View を作成します。次の例は「personName」を表示する場合です。

お名前: <span data-bind="text: personName"></span>

Knockout を作動させよう

data-bind 属性は HTML 標準ではありませんが、問題ありません。(HTML5に厳密に準拠しており、HTML4においては解釈されない属性は無視されます。) しかしブラウザは data-bind が何を意味するか知らないため、バインディングを有効にするために Knockout を作動させる必要があります。

Knockout を作動させるには、<script> タグ内に次のように記述します。

ko.applyBindings(myViewModel);

上記のスクリプトは、HTMLの最下部に配置してください。もしくは、DOMのreadyイベントハンドラでラップする (jQueryの $(function(){ ~ })などのように )ことで、<head>タグやその他どこにでも配置することができます。

これで完了です!記述されたHTMLに従って、Viewが次のように展開されます。

お名前: <span>Bob</span>

ko.applyBindings は次の引数(最大2つ)を受け取ります。

  • 1つ目の引数は、View に対してバインドすべき ViewModel です。

  • 2つ目の引数はオプションです。ViewModel をバインドする対象のDOM要素を指定することができます。
    例: ko.applyBindings(myViewModel, document.getElementById('someElementId'))
    これにより、ID「someElementId」が付与された要素と、その配下の要素に対してのみバインドを適用することができます。 1つのページに対して、部分ごとに異なる ViewModel をバインドさせるといった使い方ができます。

実にシンプルですね。

Observable

基本的な ViewModel のつくりかたと、どのようにして ViewModel のプロパティを画面に表示するかをご理解頂けたかと思います。 しかし、Knockout の最大のメリットのひとつは「 ViewModel が変更されると自動的にUIが更新される」 ということです。 Knockout はいかにして ViewModel の変更を知ることができるのでしょうか。 その答えは、ViewModel のプロパティを Observable (=オブザーバブル=監視可能) として定義することです。 Observable は特殊な JavaScript オブジェクトで、プロパティのサブスクライバー (=購読者) に対して変更を知らせ、 かつ自動的に依存関係を検知できる仕組みがあります。

先ほどの ViewModel を次のように書き換えてみましょう。

var myViewModel = {
	personName: ko.observable('ボブ'),
	personAge: ko.observable(123)
};

View を変更する必要はありません。data-bind は先程記述した内容のままで動作します。 変わったところは、変更を検知できるようになったことです。プロパティが変更されれば、View が自動的に更新されます。

Observable を読み書きする

JavaScript には getter/setter 構文がありますが、残念ながら全てのブラウザで実装されているわけではありません(IEゲフンゲフン...)。 そこで互換性を確保するため、ko.observable オブジェクトの実態は function です。

  • Observable の現在の値を取得 するには、引数なしで observable をコールします。 今回の例では、myViewModel.personName()'ボブ'を、 myViewModel.personAge()123 を返却します。

  • Observable に新しい値をセット するには、新しい値を引数に observable をコールします。 例えば、myViewModel.personName('メアリー') とすると名前が 'メアリー'に変わります。

  • 複数の Observable プロパティに新しい値をセット する場合、メソッドチェーンが便利です。 例えば、myViewModel.personName('メアリー').personAge(50) とすると名前は 'メアリー' に、年齢は 50 に変わります。

Observable の核心は監視できることにあります。言い換えると、変更通知を受け取るコードが別にあるということです。 実際それは Knockout の組み込みバインディングの多くが内部で行なっていることです。 data-bind="text: personName" と書くと、text バインディングは personName の変更通知を受け取るように登録されます。

myViewModel.personName('メアリー') を呼び出して名前を 'メアリー' に変更すると、 text バインディングは関連付けられた DOM 要素のテキストを自動的に更新します。 以上が、 ViewModel の変更が View に伝播する仕組みです。

Observable の変更通知を明示的に購読する

通常の用途では、手動で購読することはありません。入門者の方はこのセクションを読み飛ばして下さい。

Observable の変更通知を購読する方法を示します。 Observable の subscribe 関数をコールします。

myViewModel.personName.subscribe(function(newValue) {
	alert("この人の新しい名前は " + newValue + "だそうです。");
});

subscribe 関数は Knockout ライブラリの中で非常に多く使用されています。
また、購読を止める必要がある場合は、次のように dispose 関数を呼び出します。

var subscription = myViewModel.personName.subscribe(function(newValue) {
	// なにかする
});
// ...その後
subscription.dispose(); // もう通知は不要です

ほとんどの場面において、このように購読する必要はありません。 なぜなら、組み込みバインディング及びテンプレートシステムが購読の管理をしてくれるからです。

原文はこちら

side menu