Beldar
Beldar

Reputation: 481

Meteor collection not updating subscription on client

I'm quite new on Meteor and Mongo and even if I don't want it, I need some relations.

I have a Collection called Feeds and another called UserFeeds where I have a feedid and a userid, and I publish the user feeds on the server like this:

Meteor.publish('feeds', function(){
    return Feeds.find({_id:{$in:_.pluck(UserFeeds.find({user:this.userId}).fetch(),'feedid')}});
});

I find the user on UserFeeds, fetch it (returns an array) and pluck it to have only the feedid field, and then find those feeds on the Feeds collection.

And subscribe on the client like this:

Deps.autorun(function(){
   Meteor.subscribe("feeds");
});

The problem is that when I add a new feed and a new userfeed the client doesn't receive the change, but when I refresh the page the new feed does appear.

Any idea of what I'm missing here?

Thanks.

Upvotes: 9

Views: 8253

Answers (4)

Mitar
Mitar

Reputation: 7080

You can use the reactive-publish package (I am one of authors). It allows you to create publish endpoints which depend on the result of another query. In your case, query on UserFeeds.

Meteor.publish('feeds', function () {
    this.autorun(function (computation) {
        var feeds = _.pluck(UserFeeds.find({user: this.userId}, {fields: {feedid: 1}}).fetch(), 'feedid');
        return Feeds.find({_id: {$in: feeds}});
    });
});

The important part is that you limit the UserFeeds fields only to feedid to make sure autorun does not rerun when some other field changes in UserFeeds, a field you do not care about.

Upvotes: 0

zorlak
zorlak

Reputation: 1404

I've run into this, too. It turns out publish functions on the server don't re-run reactively: if they return a Collection cursor, as you're doing (and as most publish functions do), then the publish function will run once and Meteor will store the cursor and send down updates only when the contents of the cursor change. The important thing here is that Meteor will not re-run the publish function, nor, therefore, the Collection.find(query), when query changes. If you want the publish function to re-run, then the way I've done it so far is to set up the publish function to receive an argument. That way the client, whose collections do update reactively, can re-subscribe reactively. The code would look something like:

// client
Meteor.subscribe('user_feeds');

Deps.autorun(function(){ 
  var allFeeds = UserFeeds.find({user: Meteor.userId()}).fetch();
  var feedIds = _.pluck(allFeeds,'feedid');
  Meteor.subscribe('feeds',feedids);
});

// server
Meteor.publish('feeds',function(feedids) {
  return Feeds.find({_id: {$in: feedids}});
});

I believe the Meteorite package publish-with-relations is designed to solve this problem, although I haven't used it.

EDIT: I believe the publish function will re-run when the userId changes, which means that you can have a server-side check to make sure the user is logged in before publishing sensitive data.

Upvotes: 16

Kristoffer K
Kristoffer K

Reputation: 2053

I think your problem is that .fetch() which you use here…

UserFeeds.find({user:this.userId}).fetch()

…removes the reactivity.

.fetch() returns an array instead of a cursor, and that array won't be reactive.

http://docs.meteor.com/#fetch

Upvotes: 11

real.netfly
real.netfly

Reputation: 11

try this ...

Meteor.autosubscribe(function(){
    Meteor.subscribe("feeds");
});

and in the Template JS ...

Template.templateName.feeds = function() 
  return Feeds.find() # or any specific call
};

in the HTML ...

{{#each feeds}}
   do some stuff
{{else}}
   no feed
{{/each}}

Upvotes: 1

Related Questions