Encontrei esse problema ao tentar criar um recurso de blog simples no Meteor, onde tenho 2 coleções relacionadas entre si. Um é chamado news
e o outro é chamado images
. Cada notícia terá uma imageId
referência à imagem a ela associada. Usei o CollectionFS para a images
coleção.
No começo fiz uma maneira ingênua de publicar a notícia com a imagem. Onde eu consultar a lista de imageIds
do news
e retornar Images
correspondente a esses ids. Aqui está o meu código:
Meteor.publish('newsWithImage', function(options) {
if (options) check(options, { limit: Number });
else options = { limit: 5 };
options.sort = { createdAt: -1 };
var news = News.find({}, options);
var imageIds = news.map( function(news) { return news.image_id });
// Naive join
// if image is updated, news won't update the new image until reload.
return [
news,
Images.find({_id: {$in: imageIds}})
];
});
O problema é que, sempre que carrego uma nova postagem, apenas a news
coleção no cliente recebe a atualização, enquanto a imagem permanece inalterada. Como resultado, a nova postagem apareceria com um espaço vazio onde a imagem pertence. Isso acontece porque o método de publicação no servidor não é reativo e não recalcula imageIds
quando a notícia muda.
A solução é calcular o imageIds
no cliente, de modo que sempre que o imageIds
array mudar, a assinatura da imagem seja atualizada.
Então, divido a publicação em dois métodos; um para o news
e outro para images
com uma matriz de ids como parâmetro.
No servidor:
// server/publications.js
Meteor.publish('news', function(options) {
if (options) check(options, { limit: Number });
else options = { limit: 5 };
options.sort = { createdAt: -1 };
var news = News.find({}, options);
return news;
});
Meteor.publish('imageWithIds', function(imageIds) {
return Images.find({_id: {$in: imageIds}});
});
No cliente:
Template.newsList.onCreated(function () {
var instance = this;
// autorun for news
instance.autorun( function (){
//Some setup code ...
instance.subscribe('news', options);
});
// autorun for images
instance.autorun( function() {
var imageIds = News.find().map( function(p){ return p.image_id });
console.log(imageIds);
instance.subscribe('imageWithIds', imageIds);
});
});
Aqui, estou fazendo a assinatura em nível de modelo (você pode aprender mais sobre isso aqui ). é semelhante a , exceto que também é específico para este modelo. O que o autorun faz é reexecutar a função que você passa para ele toda vez que os dados dentro da função mudam.instance.autorun
Tracker.autorun
Portanto, ao inserir uma nova notícia, a imageIds
execução automática da imagem mudaria (aumentaria em um valor). Isso faria com que a função dentro da execução automática fosse executada novamente e a assinatura da imagem fosse atualizada.
* Note que a assinatura da publicação de ‘notícias’ provavelmente não precisa estar dentro do método autorun no caso normal, mas aqui meus options
parâmetros são reativos, então eu quero atualizar a assinatura sempre que options
mudar.
Se você deseja obter uma visão geral mais completa de ingressar em coleções de forma reativa, consulte o artigo de Sasha Grief em Discover Meteor.