今回は、
Meteorアプリケーションでは、
その仕組みは、
前回、
publish/subscribeによるデータ同期
Meteorでは、Meteor.
とMeteor.
を使用し、

publish
とsubscribe
の説明のため、
var Employees = new Meteor.Collection('employees');
サーバ側で実行されるMeteor.
は、
Meteor.publish("emp", function () {
// 60歳以上の従業員データを返す
return Employees.find({ age: { $gte: 60 }});
});
引数として渡す関数オブジェクトは、
対してMeteor.
はクライアント側で呼び出され、publish
によって公開されたデータを
Meteor.subscribe('emp');
ここに挙げた例では、
publish
とsubscribe
は、
Meteor.publish()
Meteor.
の定義は以下のとおりです。
Meteor.publish(name, func)
- name…名前を表す文字列。クライアントがサブスクライブを行う際に、
この名前が使用される。 - func…この関数は、
クライアントからのサブスクライブが行われるたびに呼び出されます。
第二引数func
の目的は、Meteor.
)
Meteor.publish("emp", function () {
// 60歳以上の従業員データを返す
return Employees.find({ age: { $gte: 60 }});
});
もうひとつの方法としては、publish
内でコレクションに変更を行い、publish
内のthis
オブジェクトが持つ以下のようなメソッドを通じて行います。
added(collectionName, id, fields)
- …コレクション名、
ドキュメントのID (_idフィールド)、 追加されたドキュメント (JavaScriptオブジェクト) を引数に取り、 ドキュメントが追加されたことをクライアントに通知します。 changed(collectionName, id, fields)
- …コレクション名、
ドキュメントのID (_idフィールド)、 変更されたドキュメント (JavaScriptオブジェクト) を引数に取り、 ドキュメントが変更されたことをクライアントに通知します。 removed(collectionName, id)
- …コレクション名、
ドキュメントのID (_idフィールド) を引数に取り、 ドキュメントが削除されたことをクライアントに通知します。
また、publish
内のthis
オブジェクトは、
userId
- …ログインユーザのID。ログインされていなければnull。
ready()
- …クライアントに対し、
初期データの送信が終わったことを伝える。 Meteor.
のコールバックが呼び出される。subscribe() onStop(func)
- …パブリッシュが停止した際に呼び出されるイベントハンドラを登録する。
error(error)
- …クライアントにエラーを通知する。クライアントからのサブスクライブは停止し、
Meteor.
のコールバックが呼び出される。subscribe() stop()
- …パブリッシュを停止する。
また、func
は引数を取ることもできます。func
に引数として渡される値は、Meteor.
で渡したものになります。よくある例としては、
Meteor.subscribe()
Meteor.
は、Meteor.
の定義は以下のようになっています。
Meteor.subscribe(name [, args...][, callbacks])
- name
- …サブスクリプションの名称
( publish
で指定した名前) - args
- …
Meteor.
に渡す引数。任意の数の引数を渡すことができます。publish() - callbacks
- …関数、
もしくはJavaScriptオブジェクトを指定出来ます。JavaScriptオブジェクトを指定する場合、 onReady
(初期化完了時に呼び出される) や onError
(エラー時に呼び出される) というコールバックプロパティを持つことができます。関数を指定した場合は、 onReady
を指定したのと同様になります。
また、
- stop()
- …サブスクリプションを停止することができます。
- ready()
- …このサブスクリプションが
「準備完了」 状態になっているかどうか ( publish
の節におけるready()
メソッドの説明を御覧ください)を返します。リアクティブ・ データソースであり、 この値が変化すると、 リアクティブ・ コンテキストの再評価が行われます (リアクティブ・ データソースやリアクティブ・ コンテキストについては前回の記事を御覧ください)
Meteor.autorun()を用いてsubscribeを自動的に再実行する
Meteor.
は、autorun()
は任意の関数を受け取ります。その関数は即座に実行され、autorun()
に渡された関数は自動的に再実行されます。
// 内部のリアクティブ・データソースが変化すると、関数が再実行される。
// この例では、Session内の"roomId"の値が変化すると、再実行されます。
Meteor.autorun(function () {
Meteor.subscribe("counts-by-room", Session.get("roomId"));
});
Meteor.
内でMeteor.
を使用していると、
サンプル1: 各種APIの利用例
以上で紹介したさまざまなAPIを利用している簡潔なコード例として、Meteor.
の内部でthis.
、this.
などのAPIを利用している点です。この例はチャットアプリケーションを想定しており、
// サーバ: コレクションの現時点でのサイズをパブリッシュする
Meteor.publish("counts-by-room", function (roomId) {
var self = this;
var count = 0;
var initializing = true;
var handle = Messages.find({roomId: roomId}).observeChanges({
added: function (id) {
count++;
if (!initializing)
self.changed("counts", roomId, {count: count});
},
removed: function (id) {
count--;
self.changed("counts", roomId, {count: count});
}
// movedやchangedはサイズ変更を伴わないので無視
});
// まずaddedコールバックを呼び出し、初期値をクライアントに送信する
initializing = false;
self.added("counts", roomId, {count: count});
// 初期値を送信し終わったことをクライアントに通知
self.ready();
// クライアントからのサブスクライブが終了した際に呼び出される
self.onStop(function () {
handle.stop();
});
});
// クライアント: カウントを保持するコレクションを定義する
Counts = new Meteor.Collection("counts");
// クライアント: 現在のチャットルームごとの参加人数をサブスクライブする
Meteor.autorun(function () {
Meteor.subscribe("counts-by-room", Session.get("roomId"));
});
// クライアント: コレクションからカウントを取得する
console.log("Current room has " +
Counts.findOne(Session.get("roomId")).count +
" messages.");
サンプル2: 実際に動作するサンプル
また、
前回のサンプルとの違いは、

サンプルコードは以下のリンクからダウンロードすることができます。
以下に、
まず、
<select id="filter">
<option value="">年代で絞り込む</option>
<option value="0">10歳以下</option>
<option value="10">10代</option>
<option value="20">20代</option>
<option value="30">30代</option>
<option value="40">40代</option>
<option value="50">50代</option>
<option value="60">60歳以上</option>
</select>
そして、
'change #filter': function(e, template) {
var selected = e.target.options[e.target.selectedIndex].value;
// (1) セッションに選択値をセット
Session.set('filter', selected);
},
クライアント側のコードで、Meteor.
を使用してサブスクライブを行っていますが、autorun()
に渡した関数の内部でセッションの値にアクセスしています。したがって、
// (2) 従業員データをサブスクライブする
Meteor.autorun(function() {
Meteor.subscribe('emp', Session.get('filter'));
});
そして、
// (3) 全ての従業員データを公開する
Meteor.publish("emp", function (filter) {
// クライアントから送信された検索条件に応じて、処理が変わる
// 検索条件の指定なし
if (!filter) {
return Employees.find(); // everything
}
var minAge = parseInt(filter);
switch (minAge) {
// 60歳以上
case 60:
return Employees.find({ age: { $gte: 60 }});
default:
return Employees.find({ age: { $gte: minAge, $lt: minAge + 10 }});
}
});
たったこれだけのコード追加で、
まとめ
前回と今回で、