こんにちは、村上です。
年の瀬ですね。
前回の記事に引き続き、Knockout.jsについて書きます。
前回はknockoutの導入と、簡単なobesrvableについて書きました。
今回は、その他のobservable機能の
- Observable Array
- Computed Observable
について書きます。
なお、少し前にKnockout.js v3.0.0が出ましたが、前回と今回の記事は2.x向けですのでご了承ください。
とは言え、3.0.0は裏側の処理が変わったのがメインで、Application側には影響は無いようです。
Observable Array
前回の記事で書いたko.observableは単一の値をbindするものでしたが
Arrayをbindする場合はこちらを使うことになります。
これを使うと、まとまったデータをHTMLに描画する際に楽になります。
軽くサンプルを書いてみます。今回のサンプルデータ定義はこんな感じです。
名前と得点を定義してます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
var ViewModel = function() { this.taroPoint = ko.observable(60); this.jiroPoint = ko.observable(50); this.hanakoPoint = ko.observable(40); var data = [ { name: '太郎', value: this.taroPoint }, { name: '二郎', value: this.jiroPoint }, { name: '花子', value: this.hanakoPoint } ]; }; |
個人的にobservableArrayで1番使うと思うのがforeachです。
HTML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<table> <thead> <tr> <th>名前</th> <th>得点</th> </tr> </thead> <tbody data-bind="foreach: list"> <tr> <td data-bind="text: name"></td> <td data-bind="text: value"></td> </tr> </tbody> </table> |
JS
1 2 |
// dataは上で定義してる変数です this.list = ko.observableArray(data); |
このコードは以下のようになります。
名前 | 得点 |
---|---|
1 |
data-bind="foreach: list" |
と定義すると、listにbindしてる値をループして表示してくれます。
もうちょっと拡張してみましょう。
Computed Observable
computedというbind方法もあります。
以前はdependentObservableという名前だったようです。
1 |
this.hoge = ko.computed({}, this); |
のように指定します。
これはko.observableを拡張したもので、ある特定の処理をした値を保持しておくことができます。
サンプルを示すとこんな感じです。
上の表の得点を変更出来るようにして、得点の降順で並び替えてみましょう。
それぞれの値を変更するnumber要素をつけてますので、値を変更してみてください。
名前 | 得点 |
---|---|
HTML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<table> <thead> <tr> <th>名前</th> <th>得点</th> </tr> </thead> <tbody data-bind="foreach: sortList"> <tr> <td data-bind="text: name"></td> <td data-bind="text: value"></td> </tr> </tbody> </table> |
HTMLはほぼ同じです。
foreachでbindする値をsortListに変更しました。
JS
1 2 3 4 5 6 7 8 |
var self = this; this.sortList = ko.computed(function() { // arraySortByValueは独自で作ったメソッドです。 // 内容は記載しませんが適当なバブルソートです。気になった方はソースを見てください ;) return arraySortByValue(data); }, self, { deferEvaluation: true }); |
computedの通常の定義はこんな感じになります。
第1引数で処理した内容が変数にbindされます。
第2引数には実行するcontextを設定します。
1 |
var self = this; |
contextにはthisの値を入れたselfという変数を入れています。
sortListをHTMLから呼び出していますが、そうするとこのメソッドをwindowから呼び出すことになり
thisがwindowになってしまいます。object内のデータが参照できなくなるので、selfにthisを入れてcontextを保持しています。
何言ってんの?となったらJSのthisについて調べてみると良いです。
第3引数にはcomputedのオプションをもろもろ指定できます。
deferEvaluationについては後述します。
最後に、ko.computedの処理で最近はまったことについて書いておきます。
この辺の内容は、Knockoutのリファレンスでは「ビギナーはとりあえず気にしなくていいよ!」と言ってますが
リファレンスは一度読んだ方が良い気がします。
ko.applyBindingsをした際に、ko.computedで定義されている処理を実行してbindの初期値を入れます。
ですが、処理内容によってはそのタイミングではエラーになってしまうことがあります。
特にAjaxで値を取ってきた値を使用する時などに起こりそうですね。
そういう時に、初期化のタイミングをapplyBindingsではなく実際に呼び出した際にする方法があります。
それが上で指定したdeferEvaluationです。
もう一つ。
ko.comutedでbindした値が更新されるタイミングですが
computedの値を初期化する処理で使用した値が更新されたタイミングになります。
例えば
1 2 3 |
this.hoge = ko.computed(function() { return this.fuga() + this.piyo(); }, this); |
とした場合、this.fugaやthis.piyoの値が変更されたタイミングで呼び出され、this.hogeが更新されます。
しかし
1 2 3 4 5 6 |
this.hoge = ko.computed(function() { if(/*何らかの条件*/) { return; } return this.fuga() + this.piyo(); }, this); |
のようにして始めのreturnで処理が終わると、this.fugaやthis.piyoを参照されません。
そうすると、hogeからfugaやpiyoに対しての依存関係が設定されず、fugaやpiyoを変更してもhogeが更新されなくなります。
ということを知らず、どはまりしました。
もしこの辺りの内容が間違ってたら指摘してもらえると嬉しいです。
今回はこの辺で。
では良いお年を!
フロントエンドだったりバックエンドだったりサーバだったり その時の流行りと気分でいろいろ迷走してます。