Пересекающиеся публикации Метеор
У меня есть приложение-метеор, в котором есть 2 публикации для постов. Один для всех сообщений и один для избранных. Есть 2 избранные посты - "Пост 1" и "Пост 4". Я показываю избранные посты на всех страницах, в то время как я делю все посты (включая избранные посты), отсортированные по имени. Когда я путешествую между страницами, данные из двух публикаций смешиваются и показывают неверные результаты.
Вот код:
Meteor.publish('posts', function(page) {
const skip = parseInt(page && page !== '' ? page : 0) * 3
return Posts.find({}, {
limit: 3,
skip,
sort: {
name: 1
}
});
});
Meteor.publish('featured', function() {
return Posts.find({
featured: true
}, {
sort: {
name: 1
}
});
});
На клиенте я подписываюсь на оба и отображаю данные в 2 цикла
Template.hello.onCreated(function helloOnCreated() {
const instance = this;
instance.autorun(function() {
instance.subscribe('posts', FlowRouter.getParam('page'))
instance.subscribe('featured')
});
});
Template.hello.helpers({
posts() {
return Posts.find({}, {
limit: 3,
sort: {
name: 1
}
})
},
featured_posts() {
return Posts.find({
featured: true
}, {
sort: {
name: 1
}
});
}
});
HTML-шаблон выглядит следующим образом:
<template name="hello">
<h2>Featured</h2>
{{#each featured_posts}}
{{> post}}
{{/each}}
<h2>Posts</h2>
{{#each posts}}
{{> post}}
{{/each}}
</template>
Проблема в том, что данные из двух подписок смешиваются на дисплее.
На странице 1 это показывает правильно:
Page 1
Featured
post 1
post 4
All Posts
post 1
post 2
post 3
но когда я иду на страницу 2
Page 2
Featured
post 1
post 4
All Posts -- Should be
post 1 post 4
post 4 post 5
post 5 post 6
Он показывает "пост 1" внутри "постов", которые показаны, но их не должно быть на странице 2. Когда я перехожу на страницу 3, я вижу "пост 1" и "пост 4", но их там быть не должно.
Я понимаю, как работают публикации и подписки и почему это происходит - потому что публикации объединяют набор данных. Мне интересно, есть ли работа, чтобы держать их отдельно?
2 ответа
Если я правильно понимаю, ваши страницы соответствуют нумерации страниц вашего списка "Все сообщения". Номер "страницы" отправляется как параметр вашей подписки, так что вы получите короткий список ваших сообщений.
Трудность здесь заключается в том, что ваша коллекция клиентов не имеет всех ваших документов на руках (так как вы ограничиваете их в своем 'posts'
публикации), поэтому вы не можете использовать аналогичные skip
логика как в публикации.
Как предложено в Meteor Guide > Разбивка на страницы подписок, вы можете использовать percolate:find-from-publication
Пакет Atmosphere для удобного получения документов, поступающих с вашего 'posts'
публикации, и только их.
// Server
FindFromPublication.publish('posts', function(page) {
// Same logic
const skip = parseInt(page && page !== '' ? page : 0) * 3
return Posts.find({}, {
limit: 3,
skip,
sort: {
name: 1
}
});
});
// Client (no change in subscription)
Template.hello.helpers({
posts() {
return Posts.findFromPublication('posts', {}, {
sort: {
name: 1
}
});
} // (no change in featured_posts)
});
Я бы порекомендовал использовать observe
или жеobserveChanges
так что публикуемые данные могут быть изолированы на клиенте. Документы могут быть пугающими, но на самом деле это проще, чем кажется.
Это просто вопрос создания специальной коллекции на стороне клиента и последующего использования имени этой коллекции в обратных вызовах наблюдений. Обратите внимание, что имя коллекции на стороне клиента, "featured_posts"
, используется в звонках self.added
, self.changed
а также self.removed
в публикации:
// client-side
const FeaturedPosts = new Mongo.Collection('featured_posts', {
defineMutationMethods: false
});
// server-side
Meteor.publish('featured', function() {
const self = this;
const handle = collection.find(selector, opts).observe({
added: function(doc) {
self.added('featured_posts', doc._id, doc);
},
changed: function(newDoc, oldDoc) {
self.changed('featured_posts', newDoc._id, newDoc);
},
removed: function(doc) {
self.removed('featured_posts', doc._id);
},
});
self.ready();
self.onStop(function(err) {
if (!err) {
handle.stop();
}
});
});
};