Meteor Fan (日本語情報)

JavaScriptフレームワークMeteorに関していろいろ紹介する日本語情報サイト

Meteorで初めてのデータフィルタリング制御

本記事は公式チュートリアル11の内容に沿って説明するものです。

チュートリアルの目次

  1. 初めてのMeteorアプリ作成
  2. 初めてのMeteor Spacebarsでテンプレート
  3. 初めてのMeteor Mongoコレクション
  4. 初めてのフォームとイベント処理
  5. Meteorで初めてのコレクション更新と削除
  6. 初めてのMeteorアプリのデプロイ
  7. Meteorで初めてのモバイルアプリ (スキップ)
  8. Meteorで初めてのセッション変数
  9. Meteorで初めてのユーザアカウント
  10. Meteorで初めてのパーミッション制御
  11. Meteorで初めてのデータフィルタリング制御

今回でこの一連のチュートリアルは終わりです。

データの読み込み権限について

前回のチュートリアルでは、パーミッションの制御について学びました。明確には述べませんでしたが、このパーミッション制御は書き込み権限に関するものでした。読み込みに関しては、Meteor.methodsでは制御できません。

Meteorにおけるデータの読み込みは、DDPによって裏で行なわれます。そのため、書き込みの時のようにAPI呼び出しにおける制御とは別の方法で制御します。それがpublishとsubscribeです。publishはサーバがから送信するデータを指示し、subscribeでそれを受信します。通常の(例えばRESTでよく行う)Request-Response型の通信形態とは逆向きになります。

前回までのチュートリアルではpublish/subscribeは気にする必要がありませんでしたが、それはautopublishというパッケージのおかげです。autopublishはコレクションのすべてをサーバがpublishして、クライアントがそれをsubscribeするという仕組みです。

autopublishはサーバのデータをすべてクライアントにコピーするものです。もしアプリの設計としてデータサイズが小規模でデータがすべて見えて構わない場合は、autopublishを使い続けることでも問題ないはずです。

一方、データの一部のみを見せたい場合は、autopublishではなく独自のpublishを自前で定義することになります。

PublishとSubscribeで制御する

初めにautopublishを外します。

meteor remove autopublish

で削除されます。この状態でアプリの動作を確認すると、今まで見えていたデータが見えなくなっているはずです。

まず、autopublishと同等の状態に復元します。publishはsample-app.jsのTasks = new Mongo.Collection("tasks");の直後に次のコードを追加してください。

1
2
3
4
5
if (Meteor.isServer) {
Meteor.publish("tasks", function () {
return Tasks.find();
});
}

これは”tasks”と言う名前でTasksコレクションのすべてを送信するものです。次にこれを受け取るsubscribeをsample-app.jsのif (Meteor.isClient) {}のブロック内の先頭に追加してください。

Meteor.subscribe("tasks");

これで”tasks”でpublishされたコレクションを受け取ります。この2つの修正でautopublishを外す前の状態と同等になったはずですので、動作を確認しましょう。

修正したコードはこちらと同じ(コメント除く)になっているはずです。

プライベートタスクを実装する

publishでデータのフィルタリングを制御する例として、プライベートタスクを実装します。プライベートタスクは、タスクの所有者のみが見ることのできるタスクで、privateプロパティで識別するようにします。

最初にプライベートタスクにするかどうかの切替ボタンを作ります。sample-app.htmlのtaskテンプレートのcheckboxの直後に次のコードを追加します。

1
2
3
4
5
6
7
8
9
{{#if isOwner}}
<button class="toggle-private">
{{#if private}}
Private
{{else}}
Public
{{/if}}
</button>
{{/if}}

isOwnerはこれから作るヘルパーです。トグルボタンでプライベートタスクと今まで通りのパブリックタスクを切り替えるようにしています。

プライベートタスクの場合は見ためも変えるようにしてみましょう。sample-app.htmlのtaskテンプレート内の1行目を次のように変更します。

1
<li class="{{#if checked}}checked{{/if}} {{#if private}}private{{/if}}">

次に、sample-app.jsを修正します。まず、isOwnerヘルパーをTemplate.task.events({})のブロックの前に下記のように追加します。ブロックの外です。

1
2
3
4
5
Template.task.helpers({
isOwner: function () {
return this.owner === Meteor.userId();
}
});

さらに、トグルボタンのイベント処理をTemplate.task.events.({})のブロック内の最後に追加します(追加前の最後のアイテムにカンマ”,”をつけて区切ることを忘れずに)。

1
2
3
"click .toggle-private": function () {
Meteor.call("setPrivate", this._id, ! this.private);
}

このsetPrivateはMeteor.methods({})のブロック内の最後に追加します(追加前の最後のアイテムにカンマ”,”をつけて区切ることを忘れずに)。

1
2
3
4
5
6
7
setPrivate: function (taskId, setToPrivate) {
var task = Tasks.findOne(taskId);
if (task.owner !== Meteor.userId()) {
throw new Meteor.Error("not-authorized");
}
Tasks.update(taskId, { $set: { private: setToPrivate } });
}

setPrivateでデータを変更できるのはそのデータの所有者のみにしたいため、まずそれをチェックしてからTasks.update()を呼び出しています。findOneはデータを一つだけ取得する関数です。

privateプロパティでpublishを変更

さて、privateプロパティができたので、プライベートタスクは他人からは見えないようにしましょう。そのためにはsample-app.jsを次のように修正します。

先ほど入力した

1
2
3
Meteor.publish("tasks", function () {
return Tasks.find();
});

を、次のコードで置き換えます。

1
2
3
4
5
6
7
8
Meteor.publish("tasks", function () {
return Tasks.find({
$or: [
{ private: {$ne: true} },
{ owner: this.userId }
]
});
});

MongoDBのクエリが多少複雑ですが、privateが設定されていないか、所有者が自分であるタスクをコレクションから抽出するようになっています。

Meteor.methodsの定義の変更

プライベートタスクができたので、タスク完了の変更やタスクの削除についてもプライベートタスクについては所有者しか操作できないように制限しましょう。

sample-app.jsのMeteor.methodのdeleteTasksetCheckedを次のように修正してください。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
deleteTask: function (taskId) {
var task = Tasks.findOne(taskId);
if (task.private && task.owner !== Meteor.userId()) {
throw new Meteor.Error("not-authorized");
}
Tasks.remove(taskId);
},
setChecked: function (taskId, setChecked) {
var task = Tasks.findOne(taskId);
if (task.private && task.owner !== Meteor.userId()) {
throw new Meteor.Error("not-authorized");
}
Tasks.update(taskId, { $set: { checked: setChecked} });
},

このコードではパブリックタスクは誰でも削除できるようになっています。パブリックタスクでも所有者のみが削除できるようにする修正も可能です。

最終的なコード

すべての修正を終えたファイルは、このHTMLファイルこのJavaScriptファイルになっているはずです。コメントや空行の有無などの違いはあってもコードとしては同一になっているかを確認するとよいでしょう。

動作を確認する

複数のユーザを作成して、それぞれプライベートタスクを作成し、互いに他人のプライベートタスクを見ることができないことを確認しましょう。

また仮にすべてのタスクをpublishするようにした場合でも、他人のプライベートタスクを完了したり削除したりできないことを確認しましょう。このとき、Optimistic UIにより一瞬変更が有効になったように見えて、すぐに戻されるという現象を確認できるかもしれません。

確認項目

  • deleteTaskメソッドが正しく例外処理すること
  • setCheckedメソッドが正しく例外処理すること

まとめと今後の学習法

以上で、一連のチュートリアルをすべて終了しました。これでMeteorの基本的な仕組みを学習できました。

Meteorには他にも例題が用意されています。

meteor create --example todos
meteor create --example localmarket

とすることでプロジェクトディレクトリが作成されますので、これらのコードを読んでみることも参考になるでしょう。

Meteorのマニュアルはhttp://docs.meteor.com/にあります。Basic Docsは分量が少なめなので素直に上から読んでみるとよいかもしれません(英語ですが)。Full APIは何か調べたいときに参照するのがよいのではないでしょうか。