現(xiàn)在用戶們可以給帖子添加評(píng)論了,讓他們互相知道討論已經(jīng)開始了是個(gè)好主意。
我們將通知帖子的作者已經(jīng)有用戶在他的帖子上添加了評(píng)論,并且提供一個(gè)鏈接可以看到評(píng)論。
這是 Meteor 真正閃光的特性之一:因?yàn)?Meteor 在默認(rèn)情況下是實(shí)時(shí)的,我們會(huì)_瞬時(shí)_看到這些 notifications。不需要用戶去刷新頁面或者做其他檢查,我們不需要寫任何特殊代碼就可以得到一個(gè)簡單的 notifications 彈出框。
當(dāng)有人在你的帖子上添加評(píng)論時(shí)我們將生成一個(gè) notification。在后面,notifications 會(huì)擴(kuò)展覆蓋很多其他情況,但是目前讓用戶知道正在發(fā)生什么就足夠了。
讓我們先創(chuàng)建一個(gè) Notifications
集合,和一個(gè)方法 createCommentNotification
,當(dāng)有人在你的帖子下添加評(píng)論時(shí)該方法會(huì)添加一個(gè) notification 到集合。
因?yàn)槲覀儗目蛻舳烁?notifications, 我們需要確定 allow
方法是防彈的。我們檢查如下內(nèi)容:
update
方法read
Notifications = new Mongo.Collection('notifications');
Notifications.allow({
update: function(userId, doc, fieldNames) {
return ownsDocument(userId, doc) &&
fieldNames.length === 1 && fieldNames[0] === 'read';
}
});
createCommentNotification = function(comment) {
var post = Posts.findOne(comment.postId);
if (comment.userId !== post.userId) {
Notifications.insert({
userId: post.userId,
postId: post._id,
commentId: comment._id,
commenterName: comment.author,
read: false
});
}
};
和帖子和評(píng)論一樣,Notifications
集合也是在服務(wù)器端和客戶端共享的。用戶看完 notifications 后,我們需要更新他們,因此需要允許更新操作。和其他部分一樣只有擁有 notification 的用戶才允許更新操作。
我們寫了個(gè)簡單的程序用來當(dāng)用戶給帖子添加評(píng)論時(shí)找到需要通知的用戶,并插入一個(gè)新的 notification。
我們已經(jīng)在服務(wù)器端方法創(chuàng)建了評(píng)論對(duì)象,我們用 comment._id = Comments.insert(comment)
來替換 return Comments.insert(comment);
。這樣就能在評(píng)論對(duì)象中保存 _id
, 然后調(diào)用 createCommentNotification
方法:
Comments = new Mongo.Collection('comments');
Meteor.methods({
commentInsert: function(commentAttributes) {
//...
comment = _.extend(commentAttributes, {
userId: user._id,
author: user.username,
submitted: new Date()
});
// update the post with the number of comments
Posts.update(comment.postId, {$inc: {commentsCount: 1}});
// create the comment, save the id
comment._id = Comments.insert(comment);
// now create a notification, informing the user that there's been a comment
createCommentNotification(comment);
return comment._id;
}
});
接下來發(fā)布 notifications:
Meteor.publish('posts', function() {
return Posts.find();
});
Meteor.publish('comments', function(postId) {
check(postId, String);
return Comments.find({postId: postId});
});
Meteor.publish('notifications', function() {
return Notifications.find();
});
在客戶端訂閱 notifications:
Router.configure({
layoutTemplate: 'layout',
loadingTemplate: 'loading',
notFoundTemplate: 'notFound',
waitOn: function() {
return [Meteor.subscribe('posts'), Meteor.subscribe('notifications')]
}
});
現(xiàn)在我們在 header 中添加一個(gè) notifications 列表。
<template name="header">
<nav class="navbar navbar-default" role="navigation">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navigation">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="{{pathFor 'postsList'}}">Microscope</a>
</div>
<div class="collapse navbar-collapse" id="navigation">
<ul class="nav navbar-nav">
{{#if currentUser}}
<li>
<a href="{{pathFor 'postSubmit'}}">Submit Post</a>
</li>
<li class="dropdown">
{{> notifications}}
</li>
{{/if}}
</ul>
<ul class="nav navbar-nav navbar-right">
{{> loginButtons}}
</ul>
</div>
</div>
</nav>
</template>
接下來創(chuàng)建 notifications
和 notificationItem
模板 (兩個(gè)模板都在 notifications.html
文件中):
<template name="notifications">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
Notifications
{{#if notificationCount}}
<span class="badge badge-inverse">{{notificationCount}}</span>
{{/if}}
<b class="caret"></b>
</a>
<ul class="notification dropdown-menu">
{{#if notificationCount}}
{{#each notifications}}
{{> notificationItem}}
{{/each}}
{{else}}
<li><span>No Notifications</span></li>
{{/if}}
</ul>
</template>
<template name="notificationItem">
<li>
<a href="{{notificationPostPath}}">
<strong>{{commenterName}}</strong> commented on your post
</a>
</li>
</template>
我們可以看到每個(gè) notification 有一個(gè)指向帖子的鏈接,而且包含注釋作者的名字。
接下來我們需要確定在 helper 中選擇了正確的 notifications 列表,并且在用戶點(diǎn)擊鏈接后將 notifications 標(biāo)記為已讀。
Template.notifications.helpers({
notifications: function() {
return Notifications.find({userId: Meteor.userId(), read: false});
},
notificationCount: function(){
return Notifications.find({userId: Meteor.userId(), read: false}).count();
}
});
Template.notificationItem.helpers({
notificationPostPath: function() {
return Router.routes.postPage.path({_id: this.postId});
}
});
Template.notificationItem.events({
'click a': function() {
Notifications.update(this._id, {$set: {read: true}});
}
});
你可能覺得 notifications 和 errors 很像,是的從結(jié)構(gòu)上看他們很相似。但有一點(diǎn)不同: 我們?yōu)?notification 創(chuàng)建了一個(gè)集合。這意味著 notification 是持久化的,他對(duì)于同一用戶不論瀏覽器刷新還是跨設(shè)備都是存在的。
試一下: 打開一個(gè)新的瀏覽器 (比如 Firefox), 然后創(chuàng)建一個(gè)新用戶, 然后在主用戶的帖子下發(fā)表一個(gè)評(píng)論 (在 Chrome 中)。你將看到如下:
Notifications 工作的很好。然后這有一個(gè)小問題:所有人都能看到我們的 notifications。
如果你的瀏覽器還開著,試一下在瀏覽器 console 中輸入以下 js 代碼:
? Notifications.find().count();
1
一個(gè)新的用戶 (當(dāng)有帖子被評(píng)論時(shí)) 不該收到任何 notifications. Notifications
集合中的內(nèi)容實(shí)際上屬于以前的用戶。
除了可能的隱私問題外,我們不能讓瀏覽器顯示每條 notifications 的原因是. 對(duì)于一個(gè)足夠規(guī)模的網(wǎng)站,這么做會(huì)很容易耗盡瀏覽器內(nèi)存,并帶來嚴(yán)重的性能問題。
我們通過 publications 來解決這個(gè)問題。我們可以通過 publications 來精確的指定哪部分集合我們想共享給其他用戶。
為了實(shí)現(xiàn)這個(gè),我們需要在 publication 中返回不同的 cursor 而不是使用 Notifications.find()
。換句話說,我們返回的 cursor 是和當(dāng)前用戶的 notificatons 相關(guān)的。
這么做足夠直接,因?yàn)?publish
函數(shù)有當(dāng)前用戶的 _id
, 它存在于 this.userId
中:
Meteor.publish('notifications', function() {
return Notifications.find({userId: this.userId, read: false});
});
現(xiàn)在看一下我們的兩個(gè)瀏覽器窗口,我們會(huì)看到兩個(gè)不同的 notifications 集合。
? Notifications.find().count();
1
? Notifications.find().count();
0
實(shí)際上,當(dāng)你登錄和登出時(shí) Notifications 集合都會(huì)變化。這是因?yàn)楫?dāng)賬戶變化時(shí) publications 會(huì)自動(dòng)重新發(fā)布。
我們的 app 功能越來越完善,當(dāng)越來越多的用戶發(fā)帖時(shí),我們的首頁會(huì)無限長。下一章我們通過分頁來解決這個(gè)問題。
更多建議: