ViewModel間のデータ共有や、Knockoutの外の世界とのデータやりとりについて、knockout-postboxを利用する方法についてまとめました。
knockout-postboxを利用したデータ共有
knockout-postboxとは
knockout-postboxは、Knockout.jsのcommiterであり、ほぼ公式ブログなKnock Me Outの著者であるRyan Niemeyer氏が作ったプラグインで、Knockoutの持つpub/subの仕組みを応用してモデル間のデータ共有を簡単に行うことができます。
インストール
bower経由のインストール、またはzipアーカイブからknockout-postbox(.min).jsを取得して、knockout.jsと一緒に読み込みます。
$ bower install knockout-postbox
<script type="text/javascript" src="knockout.js"></script>
<script type="text/javascript" src="knockout-postbox.js"></script>
使い方
データの変更をモデル間で通知する
Knockoutで監視している値の変更を通知するには、publishOn()を使います。
引数に任意のtopicを指定して下記のように指定します。
var viewModel = function() {
// var count = ko.observable(0); を以下のように変更する
var count = ko.observable(0).publishOn("shared_counter");
}
通知を受け取るには、subscribeTo()を使います。
publishOn()と同様に、通知を受け取りたいtopicを渡して下記のように指定します。
var anotherModel = function() {
var shared_count = ko.observable(0).subscribeTo("shared_counter");
}
デモ: http://jsfiddle.net/castor_4bit/myBk3/
データの変更を外部に通知する
postboxを利用したpub/subは、モデル間以外でも利用可能です。
publishされた通知を外部で受け取るには、ko.postbox.subscribe()を使用します。
ko.postbox.subscribe("shared_counter", function(value) {
// ここで受け取った value を使った処理ができます
});
また、subscribeしているModelに対して、外部からpublishすることも可能です。
ko.postbox.publish("shared_counter", 100 /* 通知する値 */);
データを同期する
複数のモデル間などで相互にpublish/subscribeするような場合には、以下のように記述できます。
var viewModel = function() {
var count = ko.observable(0).syncWith("shared_counter");
}
var anotherModel = function() {
var shared_count = ko.observable(0).syncWith("shared_counter");
}
デモ: http://jsfiddle.net/castor_4bit/u6v8A/1/
補足(オプション)
subscribeToの第2引数に true を渡すと、初期化時に最後にpublishされた値が適用されます。デフォルトでは false です。
var count = ko.observable().subscribeTo("shared_counter", true);
subscribeToの第3引数に true を渡すと、初期化時に最後にpublishされた値が適用されます。(第2引数を省略して関数を渡しても動作します)
var count = ko.observable().subscribeTo("shared_counter", true, function(value) {
return value * 2; // 常に2倍の値で同期される
});
subscribeを解除するには、unsubscribeFrom()を使います。
count.unsubscribeFrom("shared_counter")
publishOnの第2引数に true を渡すと、初期化時にも値の変更としてpublishされます。デフォルトでは true です。
// 初期値100がpublishされる
var count = ko.observable(100).publishOn("shared_counter", true);
publishOnの第3引数に比較関数を渡すと、条件を 満たさない場合 のみpublishされます。デフォルトでは === での比較結果が false の場合にpublishされます。
var count = ko.observable(100).publishOn("shared_counter", true, function(newValue, oldValue) {
return newValue < oldValue; // 新しい値が大きい場合のみ通知される
});
publishを解除するには、stopPublishingOn()を使います。
count.stopPublishingOn("shared_counter");
syncWith()にもそれぞれ同様のオプションが存在しますが、詳細は公式READMEを参照ください。
中規模以上のアプリケーションなどでは共用のコンテキストを保持するようなことが多いかもしれませんが、ページの一部分にKnockoutを適用する場合など、ViewModel同士や外部とのやりとりに依存関係を持ち込まずに実装できるという点で、結構重宝しています。