Meteor 評論

2022-06-30 13:58 更新

評論

社交新聞網(wǎng)站的目標(biāo)是創(chuàng)建一個用戶社區(qū),如果沒有提供一種方式讓人們互相交流,這將是很難做到的。因此在本章中,我們添加評論!

我們首先創(chuàng)建一個新的集來存儲評論,并在該集中添加一些初始數(shù)據(jù)。

Comments = new Mongo.Collection('comments');
// Fixture data
if (Posts.find().count() === 0) {
  var now = new Date().getTime();

  // create two users
  var tomId = Meteor.users.insert({
    profile: { name: 'Tom Coleman' }
  });
  var tom = Meteor.users.findOne(tomId);
  var sachaId = Meteor.users.insert({
    profile: { name: 'Sacha Greif' }
  });
  var sacha = Meteor.users.findOne(sachaId);

  var telescopeId = Posts.insert({
    title: 'Introducing Telescope',
    userId: sacha._id,
    author: sacha.profile.name,
    url: 'http://sachagreif.com/introducing-telescope/',
    submitted: new Date(now - 7 * 3600 * 1000)
  });

  Comments.insert({
    postId: telescopeId,
    userId: tom._id,
    author: tom.profile.name,
    submitted: new Date(now - 5 * 3600 * 1000),
    body: 'Interesting project Sacha, can I get involved?'
  });

  Comments.insert({
    postId: telescopeId,
    userId: sacha._id,
    author: sacha.profile.name,
    submitted: new Date(now - 3 * 3600 * 1000),
    body: 'You sure can Tom!'
  });

  Posts.insert({
    title: 'Meteor',
    userId: tom._id,
    author: tom.profile.name,
    url: 'http://meteor.com',
    submitted: new Date(now - 10 * 3600 * 1000)
  });

  Posts.insert({
    title: 'The Meteor Book',
    userId: tom._id,
    author: tom.profile.name,
    url: 'http://themeteorbook.com',
    submitted: new Date(now - 12 * 3600 * 1000)
  });
}

不要忘記來發(fā)布和訂閱我們這個新的集合:

Meteor.publish('posts', function() {
  return Posts.find();
});

Meteor.publish('comments', function() {
  return Comments.find();
});
Router.configure({
  layoutTemplate: 'layout',
  loadingTemplate: 'loading',
  notFoundTemplate: 'notFound',
  waitOn: function() {
    return [Meteor.subscribe('posts'), Meteor.subscribe('comments')];
  }
});

請注意,為了使用新的數(shù)據(jù),你需要命令 Meteor reset 清除數(shù)據(jù)庫。不要忘了創(chuàng)建一個新的用戶,并重新登錄!

首先,我們在數(shù)據(jù)庫中創(chuàng)建了幾個(假的)用戶,并從數(shù)據(jù)庫中用他們的 id 選擇出來。然后給第一篇添加注釋,鏈接注釋到帖子(postId)和用戶(userId)。同時(shí)我們還添加了提交日期,評論內(nèi)容和一個非規(guī)范化的作者 author 項(xiàng)。

此外我們在路由器中增加等待一個含有評論和帖子訂閱的數(shù)組。

顯示評論

把評論存到數(shù)據(jù)庫,同時(shí)還需要在評論頁上顯示。

<template name="postPage">
  {{> postItem}}
  <ul class="comments">
    {{#each comments}}
      {{> commentItem}}
    {{/each}}
  </ul>
</template>
Template.postPage.helpers({
  comments: function() {
    return Comments.find({postId: this._id});
  }
});

我們把 {{#each comments}} 塊放在帖子模板里面,所以在 comments helper 里,this 指向的是當(dāng)前帖子。要找到相關(guān)的評論,我們可通過 postId 屬性找到鏈接到該帖子的評論。

我們已經(jīng)了解 helper 和 Spacebars 后,顯示一個評論是相當(dāng)簡單的。我們將在 templates 下,創(chuàng)建一個新的 comments 目錄和一個新的 commentItem 模板,來存儲所有的評論相關(guān)的信息:

<template name="commentItem">
  <li>
    <h4>
      <span class="author">{{author}}</span>
      <span class="date">on {{submittedText}}</span>
    </h4>
    <p>{{body}}</p>
  </li>
</template>

讓我們編寫一個模板 helper 來幫助我們把 submitted 提交日期格式人性化的日期格式:

Template.commentItem.helpers({
  submittedText: function() {
    return this.submitted.toString();
  }
});

然后,我們將在每個帖子中顯示評論數(shù):

<template name="postItem">
  <div class="post">
    <div class="post-content">
      <h3><a href="{{url}}">{{title}}</a><span>{{domain}}</span></h3>
      <p>
        submitted by {{author}},
        <a href="{{pathFor 'postPage'}}">{{commentsCount}} comments</a>
        {{#if ownPost}}<a href="{{pathFor 'postEdit'}}">Edit</a>{{/if}}
      </p>
    </div>
    <a href="{{pathFor 'postPage'}}" class="discuss btn btn-default">Discuss</a>
  </div>
</template>

添加 commentsCount helper 到 post_item.js 中:

Template.postItem.helpers({
  ownPost: function() {
    return this.userId === Meteor.userId();
  },
  domain: function() {
    var a = document.createElement('a');
    a.href = this.url;
    return a.hostname;
  },
  commentsCount: function() {
    return Comments.find({postId: this._id}).count();
  }
});

現(xiàn)在您應(yīng)該能夠顯示初始的評論并看到如下的內(nèi)容:

提交新評論

讓用戶創(chuàng)建新的評論,這個過程將是非常類似過去我們允許用戶創(chuàng)建新的帖子。

首先我們通過在每個帖子底部增加一個提交框:

<template name="postPage">
  {{> postItem}}

  <ul class="comments">
    {{#each comments}}
      {{> commentItem}}
    {{/each}}
  </ul>

  {{#if currentUser}}
    {{> commentSubmit}}
  {{else}}
    <p>Please log in to leave a comment.</p>
  {{/if}}
</template>

然后創(chuàng)建評論表單模板:

<template name="commentSubmit">
  <form name="comment" class="comment-form form">
    <div class="form-group {{errorClass 'body'}}">
        <div class="controls">
            <label for="body">Comment on this post</label>
            <textarea name="body" id="body" class="form-control" rows="3"></textarea>
            <span class="help-block">{{errorMessage 'body'}}</span>
        </div>
    </div>
    <button type="submit" class="btn btn-primary">Add Comment</button>
  </form>
</template>

comment_submit.js 中調(diào)用 comment 方法,提交新的評論,這是類似于過去提交帖子的方法:

Template.commentSubmit.onCreated(function() {
  Session.set('commentSubmitErrors', {});
});

Template.commentSubmit.helpers({
  errorMessage: function(field) {
    return Session.get('commentSubmitErrors')[field];
  },
  errorClass: function (field) {
    return !!Session.get('commentSubmitErrors')[field] ? 'has-error' : '';
  }
});

Template.commentSubmit.events({
  'submit form': function(e, template) {
    e.preventDefault();

    var $body = $(e.target).find('[name=body]');
    var comment = {
      body: $body.val(),
      postId: template.data._id
    };

    var errors = {};
    if (! comment.body) {
      errors.body = "Please write some content";
      return Session.set('commentSubmitErrors', errors);
    }

    Meteor.call('commentInsert', comment, function(error, commentId) {
      if (error){
        throwError(error.reason);
      } else {
        $body.val('');
      }
    });
  }
});

類似以前 post 服務(wù)器端的 Meteor 方法,我們將建立一個 comment 的 Meteor 方法來創(chuàng)建評論,檢查正確性后,插入到評論集合。

Comments = new Mongo.Collection('comments');

Meteor.methods({
  commentInsert: function(commentAttributes) {
    check(this.userId, String);
    check(commentAttributes, {
      postId: String,
      body: String
    });

    var user = Meteor.user();
    var post = Posts.findOne(commentAttributes.postId);

    if (!post)
      throw new Meteor.Error('invalid-comment', 'You must comment on a post');

    comment = _.extend(commentAttributes, {
      userId: user._id,
      author: user.username,
      submitted: new Date()
    });

    return Comments.insert(comment);
  }
});

這里沒做什么太花哨的,只是檢查該用戶是否已經(jīng)登錄,該評論有一個內(nèi)容,并且鏈接到一個帖子。

控制訂閱評論

如同以往一樣,我們將發(fā)布屬于所有帖子的全部評論到每個連接的客戶端。這似乎有點(diǎn)浪費(fèi)。畢竟在任何給定的時(shí)間段,實(shí)際上只使用該數(shù)據(jù)的一小部分。因此讓我們提高發(fā)布和訂閱評論的精度。

如果仔細(xì)想想,我們需要訂閱 comments 評論發(fā)布的時(shí)間,是當(dāng)用戶訪問一個帖子的頁面,而此刻只需要加載這個帖子的評論子集。

第一步將改變我們訂閱評論的方式。目前是路由器級訂閱,這意味著當(dāng)路由器初始化時(shí),加載所有數(shù)據(jù)。

但是現(xiàn)在希望我們的訂閱依賴于路徑參數(shù),并且參數(shù)可以在任何時(shí)候改變。因此需要將我們的訂閱代碼從路由器級改到路徑級。

這樣做的另一個后果:每當(dāng)打開路徑時(shí)加載數(shù)據(jù),而不是初始化應(yīng)用時(shí)加載它。這意味著你在程序內(nèi)瀏覽時(shí),會等待加載時(shí)間。除非你打算加載全部內(nèi)容到客戶端,這是一個不可避免的缺點(diǎn)。

首先,在 configure 塊中通過刪除 Meteor.subscribe('comments'),停止預(yù)裝全部評論(換句話說,要回以前的方法):

Router.configure({
  layoutTemplate: 'layout',
  loadingTemplate: 'loading',
  notFoundTemplate: 'notFound',
  waitOn: function() {
    return Meteor.subscribe('posts');
  }
});

我們將為 postPage 添加一個新的路徑級的 waitOn 函數(shù):

//...

Router.route('/posts/:_id', {
  name: 'postPage',
  waitOn: function() {
    return Meteor.subscribe('comments', this.params._id);
  },
  data: function() { return Posts.findOne(this.params._id); }
});

//...

我們將 this.params._id 作為參數(shù)傳遞給訂閱。所以讓我們用這個新信息來確保限制評論屬于當(dāng)前帖子:

Meteor.publish('posts', function() {
  return Posts.find();
});

Meteor.publish('comments', function(postId) {
  check(postId, String);
  return Comments.find({postId: postId});
});

這里有一個問題:當(dāng)我們返回主頁,顯示所有的帖子有0條評論:

評論計(jì)數(shù)

這個問題的原因是:我們只裝載 postPage 路徑上的評論,所以當(dāng)我們調(diào)用 Comments.find({postId: this._id})commentsCount helper,Meteor 找不到必要的客戶端數(shù)據(jù)為我們提供計(jì)數(shù)值。

處理這一問題的最佳辦法是非規(guī)范化的評論計(jì)數(shù)加到帖子中(如果你不明白這意味著什么,不用擔(dān)心,接下來的附錄會詳解?。U缥覀儗⒖吹降?,代碼中復(fù)雜性稍微增加,但從不發(fā)布帖子列表的_全部_評論中,得到的執(zhí)行性能改善是值得的。

我們將通過在 post 數(shù)據(jù)結(jié)構(gòu)中增加一個 commentsCount 屬性來實(shí)現(xiàn)這一目標(biāo)。首先,更新帖子的測試數(shù)據(jù)(用 meteor reset 去重載它們 —— 不要忘了之后重新創(chuàng)建你的帳戶):

// 測試數(shù)據(jù)
if (Posts.find().count() === 0) {
  var now = new Date().getTime();

  // create two users
  var tomId = Meteor.users.insert({
    profile: { name: 'Tom Coleman' }
  });
  var tom = Meteor.users.findOne(tomId);
  var sachaId = Meteor.users.insert({
    profile: { name: 'Sacha Greif' }
  });
  var sacha = Meteor.users.findOne(sachaId);

  var telescopeId = Posts.insert({
    title: 'Introducing Telescope',
    userId: sacha._id,
    author: sacha.profile.name,
    url: 'http://sachagreif.com/introducing-telescope/',
    submitted: new Date(now - 7 * 3600 * 1000),
    commentsCount: 2
  });

  Comments.insert({
    postId: telescopeId,
    userId: tom._id,
    author: tom.profile.name,
    submitted: new Date(now - 5 * 3600 * 1000),
    body: 'Interesting project Sacha, can I get involved?'
  });

  Comments.insert({
    postId: telescopeId,
    userId: sacha._id,
    author: sacha.profile.name,
    submitted: new Date(now - 3 * 3600 * 1000),
    body: 'You sure can Tom!'
  });

  Posts.insert({
    title: 'Meteor',
    userId: tom._id,
    author: tom.profile.name,
    url: 'http://meteor.com',
    submitted: new Date(now - 10 * 3600 * 1000),
    commentsCount: 0
  });

  Posts.insert({
    title: 'The Meteor Book',
    userId: tom._id,
    author: tom.profile.name,
    url: 'http://themeteorbook.com',
    submitted: new Date(now - 12 * 3600 * 1000),
    commentsCount: 0
  });
}

像往常一樣,更新測試數(shù)據(jù)文件時(shí),你必須用 meteor reset 重置數(shù)據(jù)庫,以確保它再次運(yùn)行。

然后,我們要確保所有新帖子的評論計(jì)數(shù)從0開始:

//...

var post = _.extend(postAttributes, {
  userId: user._id,
  author: user.username,
  submitted: new Date(),
  commentsCount: 0
});

var postId = Posts.insert(post);

//...

當(dāng)我們創(chuàng)建一個新的評論時(shí),使用 Mongo 的 $inc 操作(給一個數(shù)字字段值加一)更新相關(guān)的 commentsCount

//...

comment = _.extend(commentAttributes, {
  userId: user._id,
  author: user.username,
  submitted: new Date()
});

// 更新帖子的評論數(shù)
Posts.update(comment.postId, {$inc: {commentsCount: 1}});

return Comments.insert(comment);

//...

最后只需簡單地刪除 client/templates/posts/post_item.jscommentsCount helper,因?yàn)樵撝悼梢詮奶又械玫健?/p>

現(xiàn)在用戶可以互相對話,如果他們錯過了新的評論,這將是不可原諒的。接下來的章節(jié)將告訴你如何實(shí)現(xiàn)通知,以防止這個情況發(fā)生!

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號