年明け最初のエントリーでは、軽めの内容として、何回かに分けて、Knockout.jsの小技(HTMLの画面開発でありがちな実装について、Knockoutでの実装例)を紹介していきたいと思います。
第1回は、チェックボックスの全選択on/off(いわゆるtoggle)機能です。
完成形
リポジトリ
https://github.com/tq-jappy/knockout-samples
本題とは逸れますが、依存するモジュールの管理のためにRequireJSを使っています。大きな画面の開発になると、JavaScriptコードを複数のファイルに分割する必要がでてきたり、それに伴い依存モジュールやscriptタグの記述順序などで余計な気を回さないとならなくなってくるので、こういった機構を早目に準備していると、後で痛い目を見なくても済むかもしれません。というより、後付けで導入しにくい機構な気がします。
RequireJSはRequireJSで、JavaScriptファイルの最適化(r.js)なんかに一手間必要になったりと面倒なところもあるのですが…。
View
まずはView。ヘッダは省略しますが、メインは以下の部分。使っているのは3種類の標準バインディング。
- 項目をループ処理する時のforeach
- チェックボックスの選択状態を管理するchecked
- テキストの表示にtext
<input type="checkbox" data-bind="checked: toggleAll" /> Toggle All<br/> <ul data-bind="foreach: items"> <li> <input type="checkbox" data-bind="checked: checked" /> <span data-bind="text: name"></span> </li> </ul>
「Toggle All」をクリックすると、3つの項目すべてのチェックボックスのon/offが切り替わるようにします。jQueryと違い、idの命名に頭を悩ませたりしなくて済むのが楽ですね。
Model
function Item(name, checked) { this.name = ko.observable(name); this.checked = ko.observable(checked || false); }
表示名(name)と、チェックされたかどうか(checked)をそれぞれobservableで持ちます。
ViewModel
function ViewModel() { this.toggleAll = ko.observable(false); this.items = ko.observableArray([ new Item("Foo 1"), new Item("Foo 2", true), new Item("Foo 3") ]); this.toggleAll.subscribe(function(newValue) { this.items().forEach(function(item) { item.checked(newValue); }); }, this); }
ViewModelでは、項目(items)をobservableArrayで管理します。初期状態では、"Foo 2"だけがチェックされている状態にしてみました。
そして今回の肝がsubscribe。コレを使うと、observableの値が変化した場合の処理を定義することができます。全選択用チェックボックスと、各項目の選択用チェックボックスはコンテキスト(前者はViewModel, 後者はItemモデル)が異なるため、ko.computedで何とかやろうとするのは結構大変だと思います。subscribeが呼び出す関数の引数にはobservableの変更後の値が渡るので、それを使ってすべての項目のcheckedの値を書き換えることができます。