Невозможно отобразить данные из базы данных

Я создаю форум с Meteor и хотел бы отображать комментарии к ответам. И ответы находятся на той же странице, что и отдельный вопрос. Я могу сохранить отправленные комментарии (я могу проверить базу данных mongo и вижу, что успешно отправляю комментарии), но я не могу отобразить их с помощью шаблонов. Я бы подумал, что проблема как-то связана с публикациями и подписками, но я не могу найти ошибку. Ниже приведены соответствующие фрагменты кода.

answer_item.js

Template.answerItem.helpers({
  submittedText: function() {
    return this.submitted.toString();
  },
  comments: function() {
    return Comments.find({answerId: this._id});
  }
});

answer_item.html

<template name="answerItem">
...
    <ul class="comments">
      {{#each comments}}
        {{> commentItem}}
      {{/each}}
    </ul>
...
</template>

comment_item.html

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

  </li>
</template>

comment_item.js

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

библиотека/коллекции/comment.js

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

Meteor.methods({
  commentInsert: function(commentAttributes) {
    check(this.userId, String);
    check(commentAttributes, {
      answerId: String,
      body: String
    });
    var user = Meteor.user();
    var answer = Answers.findOne(commentAttributes.answerId);

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

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

    Answers.update(comment.answerId, {$inc: {commentsCount: 1}});

    comment._id = Comments.insert(comment);

    return comment._id
  }
});

router.js

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

QuestionsListController = RouteController.extend({
  template: 'questionsList',
  increment: 5,
  questionsLimit: function() {
    return parseInt(this.params.questionsLimit) || this.increment;
  },
  findOptions: function() {
    return {sort: this.sort, limit: this.questionsLimit()};
  },
  subscriptions: function() {
    this.questionsSub = Meteor.subscribe('questions', this.findOptions());
  },
  questions: function() {
    return Questions.find({}, this.findOptions());
  },
  data: function() {
    var self = this;
    return {
      questions: self.questions(),
      ready: self.questionsSub.ready,
      nextPath: function() {
        if (self.questions().count() === self.questionsLimit())
          return self.nextPath();
      }
    };
  }
});

NewQuestionsController = QuestionsListController.extend({
  sort: {submitted: -1, _id: -1},
  nextPath: function() {
    return Router.routes.newQuestions.path({questionsLimit: this.questionsLimit() + this.increment})
  }
});

FollowedQuestionsController = QuestionsListController.extend({
  sort: {follows: -1, submitted: -1, _id: -1},
  nextPath: function() {
    return Router.routes.followedQuestions.path({questionsLimit: this.questionsLimit() + this.increment})
  }
});

Router.route('/', {
  name: 'home',
  controller: NewQuestionsController
});

Router.route('/new/:questionsLimit?', {name: 'newQuestions'});

Router.route('/followed/:questionsLimit?', {name: 'followedQuestions'});


Router.route('/questions/:_id', {
  name: 'questionPage',
  waitOn: function() {
    return [
      Meteor.subscribe('singleQuestion', this.params._id),
      Meteor.subscribe('answers', this.params._id),
      Meteor.subscribe('comments', this.params._id)
    ];
  },
  data: function() { return Questions.findOne(this.params._id); }
});

Router.route('/questions/:_id/edit', {
  name: 'questionEdit',
  waitOn: function() {
    return Meteor.subscribe('singleQuestion', this.params._id);
  },
  data: function() { return Questions.findOne(this.params._id); }
});

Router.route('/submit', {name: 'questionSubmit'});

var requireLogin = function() {
  if (! Meteor.user()) {
    if (Meteor.loggingIn()) {
      this.render(this.loadingTemplate);
    } else {
      this.render('accessDenied');
    }
  } else {
    this.next();
  }
}

Router.onBeforeAction('dataNotFound', {only: 'questionPage'});
Router.onBeforeAction(requireLogin, {only: 'questionSubmit'});

сервер/publications.js

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

person jro    schedule 10.08.2015    source источник
comment
Вы показываете только одну из трех своих публикаций. Однако из вашей модели кажется маловероятным, что вопросы, ответы и комментарии будут иметь один и тот же внешний ключ (this.params.id), тем более что ваша публикация, похоже, указывает, что это answerId, а не questionId. Предполагая, что на каждый вопрос может быть несколько ответов, у вас будет несколько answerId для каждого questionId. Дважды проверьте свой маршрут.   -  person Michel Floyd    schedule 11.08.2015
comment
Как мне получить только комментарии для соответствующего ответа. («комментарии», «что здесь происходит?»). Я также обновил router.js, включив в него весь файл.   -  person jro    schedule 11.08.2015
comment
Я не могу сказать, не видя, как вы организовали свои коллекции вопросов, ответов и комментариев. Если ответы указывают на вопросы через questionId, а комментарии указывают на ответы через answerId (как предполагает ваш код), то ответ @Brian ниже является одним из решений. Вы также можете найти atmospherejs.com/reywood/publish-composite полезным, так как он может публиковать связанные документы , по существу имитируя реляционные соединения.   -  person Michel Floyd    schedule 11.08.2015


Ответы (1)


Похоже, вам нужно запустить отдельный запрос в публикации комментариев, чтобы получить список всех ответов на данный вопрос, а затем использовать результаты этого запроса, чтобы получить список всех комментариев для всех ответов.

Meteor.publish('comments', function(questionId) {
    check(questionId, String);
    var answerIds = _.pluck(Answers.find({'questionId': questionId}, {fields: {_id: 1}}).fetch(), '_id');
    return Comments.find({answerId: {$in: answerIds});
});

ИЗМЕНИТЬ

У меня есть аналогичная функция в приложении, над которым я сейчас работаю, с той же проблемой, с которой вы столкнулись. Вчера я потратил на это несколько часов и пришел к выводу, что проблема связана с тем, что оператор _.pluck преобразует результаты из курсора Answers в массив, что предотвращает реактивность функции публикации.

Изучив несколько решений, лучшим, который я нашел, был пакет публикации составного. Синтаксис немного подробен, но он выполняет свою работу. Чтобы все это работало правильно, вам нужно объединить все функции публикации для вопроса, ответов и комментариев в одну функцию публикации. Под прикрытием он создает observeChanges наблюдателей за каждым из ответов под вопросом, поэтому он может быть реактивным.

Meteor.publishComposite('question', function(questionId) {
    return {
        find: function() {
            return Questions.find({_id: questionId});
        },
        children: [
            {
                find: function(question) {
                    return Answers.find({questionId: question._id});
                },
                children: [
                    {
                        find: function(answer, question) {
                            return Comments.find({answerId: answer._id});
                        }
                    }
                ]
            }
        ]
    }
});
person Brian Shamblen    schedule 11.08.2015
comment
Поскольку questionId не является реактивным, комментарии не будут обновляться в режиме реального времени. Вам нужно будет перезагружать подписку на комментарии всякий раз, когда вы публикуете новый комментарий. template.subscribe('comments', questionId); после добавления нового комментария. - person Brian Shamblen; 11.08.2015
comment
@JonRoby Я обновил свой ответ выше, добавив немного больше информации. Дайте знать, если у вас появятся вопросы. - person Brian Shamblen; 18.08.2015