KnockoutでUIの表示を動的に変更する
JavaScriptで保持しているデータをHTMLに表示しているとき、データに変更があったことをHTMLに伝えるにはどのような方法があるでしょうか。基本的には、JavaScriptでDOMを操作してUIに反映させるしかありません。また、入力されたデータをJavaScriptで受け取る際にもDOMからデータを取得する必要があります。
Knockout(MITライセンス)というライブラリを使うと、JavaScriptで保持しているデータとHTMLを直接関連付け、相互にデータを反映させて同期を取ることができます。従来のようなDOMを操作する煩わしさから解放され、JavaScriptのモデルのプロパティを更新するだけで自動的にHTMLを更新したり、またフォームに入力されたデータをJavaScriptのモデルに自動的に取り込むことができるようになります。Knockoutはクライアント側のライブラリなので、サーバは必要ありません。IE6以上など現在使われているほとんどのブラウザで動作します。
モデルと表示を司るライブラリとしてはBackbone.js(MITライセンス)が有名で、APIの仕様もトレンドに沿った正統進化形と言えますが、Knockoutはさらに先の未来を見据え、HTMLとJavaScriptは本来こうあるべきだという提案をしています。本稿ではBackbone.jsは紹介せず、より斬新なコンセプトを持つKnockoutを紹介します。Backbone.jsについて詳しく知りたい方はWeb上の文献などを参考にしてください。
Knockoutの使い方
Knockoutは、一般的な「モデル - ビュー - コントローラ」(Model-View-Controllerの頭文字を取ってMVC)ではなく、「モデル - ビュー - ビューモデル」(Model-View-View Modelの頭文字を取ってMVVM)という設計パターンを採用しています。
モデルとは、アプリケーションが保持しているデータのうちUIとは完全に独立したもので、JavaScriptのクラスやオブジェクトなどに相当します。モデルではデータの実体やアプリケーションのロジックなどを定義し、表示に関わる処理は含めません。ビューモデルとは、表示に直接関係するJavaScriptデータのことをいい、UIの表示形式に沿ったデータ構造を持つモデルであるとも言えます。ビューとは、ビューモデルを表示するUI、つまりHTMLの一部分です。ビューはビューモデルの内容をそのまま表示し、UIで操作が行われた時にビューモデルに通知します。ビューとモデルの橋渡しの役目をするのがビューモデルです。これら3つの部品を使い分けてアプリケーションを作ります。
ライブラリの読み込み
ダウンロードページから最新のライブラリをダウンロードし、<script>
タグで読み込みます。
モデルからビューへの反映
まず、次のようにHTMLを作成します。
<!doctype html> <html lang="ja"> <head> <meta charset="utf-8"> <title>test</title> </head> <body> <div> <p>姓: <strong data-bind="text: lastName"></strong></p> <p>名: <strong data-bind="text: firstName"></strong></p> </div> <script src="js/knockout-2.0.0.js"></script> <script type="text/javascript" charset="utf-8" src="js/main.js"></script> </body> </html>
data-bind属性が付けられた箇所に値を反映するには、スクリプトで次のようにビューモデルを定義します。表示するプロパティは、コンストラクタ内でthis
のプロパティに代入することで定義します。
# ビューモデル class AppViewModel constructor: -> @firstName = "龍馬" @lastName = "坂本" # ビューモデルとHTMLを結び付ける ko.applyBindings new AppViewModel
ビューからモデルへの反映
ビューで入力されたデータをモデルに反映するには、次のように<input>
タグを追加します。
<!doctype html> <html lang="ja"> <head> <meta charset="utf-8"> <title>test</title> </head> <body> <div> <p>姓: <strong data-bind="text: lastName"></strong></p> <p>名: <strong data-bind="text: firstName"></strong></p> <p>姓: <input data-bind="value: lastName"></p> <p>名: <input data-bind="value: firstName"></p> </div> <script src="js/knockout-2.0.0.js"></script> <script type="text/javascript" charset="utf-8" src="js/main.js"></script> </body> </html>
そして、反映したいプロパティを定義する時にko.observable()
を付けます。変更を動的にビューに反映したり、ビューに入力された値を受け取るためのプロパティはko.observable()
を使って定義します。
class AppViewModel constructor: -> @firstName = ko.observable "龍馬" @lastName = ko.observable "坂本" ko.applyBindings new AppViewModel
これをブラウザで開き、テキストフィールドの文字列を編集してフォーカスを移動すると即座にモデルに変更が反映され、そしてモデルの変更が即座に表示に反映されます。ko.observable()
で定義したプロパティの値を取得する時は必ず次のようにメソッド呼び出し形式を使います。
firstName = @firstName() lastName = @lastName()