whales whales - 3 months ago 19
Javascript Question

Necessary to pass limit to data when subscription already limits the dataset?

In a example about pagination, different number is passed to the route in terms of how many results should be displayed.

// lib/router.js
Router.route('/:postsLimit?', {
name: 'postsList',
waitOn: function() {
var limit = parseInt(this.params.postsLimit) || 5;
return Meteor.subscribe('posts', {sort: {submitted: -1}, limit: limit});
},
data: function() {
var limit = parseInt(this.params.postsLimit) || 5;
return {
posts: Posts.find({}, {sort: {submitted: -1}, limit: limit})
};
}
});


I am wondering if it's necessary to still give the Posts.find() arguments in the data attribute. It seems kinda redundant here because the subscribe in waitOn already limit the dataset that i am getting back from the server. I know that data is used to provide data context for different parts of my templates. However, providing the same arguments here to data Posts.find() just seems redundant. I have tried just using Posts.find() without the argument and it worked.

Also, I am wondering what is a way to access all the data in router? I am accessing the Posts collections outside the individual route and I thought that I would be able to access all the data in Posts collections. However, it returned with a count 0.

// lib/router.js
var totalPostsCount = Posts.find().count()
console.log('total count?')
console.log(totalPostsCount) // output 0


PostsListController = RouteController.extend({
template: 'postsList',
increment: 5,
postsLimit: function() {
return parseInt(this.params.postsLimit) || this.increment;
},
findOptions: function() {
return {sort: {submitted: -1}, limit: this.postsLimit()};
},
subscriptions: function() {
this.postsSub = Meteor.subscribe('posts', this.findOptions());
},
posts: function() {
// console.log("is find limited to subscriptions too??");
// console.log(Posts.find().count())
return Posts.find({}, this.findOptions());
},
data: function() {
// console.log('is data limited to this?')
// console.log(Posts.find().count())

var hasMore = this.posts().count() === this.postsLimit();
var adjustOrNot = this.posts()
if (!hasMore){
var nextPath = this.route.path({postsLimit: this.posts().count()});
}else{
var nextPath = this.route.path({postsLimit: this.postsLimit() + this.increment});
}
return {
posts: this.posts(),
ready:this.postsSub.ready(),
nextPath: hasMore ? nextPath : null
};
}
});

//...

Router.route('/:postsLimit?', {
name: 'postsList'
});


Edit 1: code for getting all posts:

// server
Meteor.publish('allPosts',function(){

return Posts.find({});
})


// lib/router.js
if (Meteor.isClient){
var handle = Meteor.subscribe('allPosts');
if (handle.ready()) {
// This logs the actual count of posts
console.log(Posts.find().count());
} else {

console.log('not ready yet');
}
}


This only outputs 'not ready yet' in the console and it's not changing even when the page finish loading.

Thanks a lot for the help.

Edit 2: possible solutions

I tried wrapping the reactive source ready() inside a computation such as Tracker.autorun() and now it worked.

if (Meteor.isClient){
var handle = Meteor.subscribe('allPosts');

Tracker.autorun(function(){
var status = handle.ready();
if (status){
console.log(Posts.find().count())
}else{
console.log('not ready yet')
}
})

}

Answer

Yes, you should pass limit and other query options to the front-end query too, regardless of your subscription. This is because if you were to have multiple subscriptions to the same collection active at the same time (as is often done in larger apps) the subscribed records end up all in the same Minimongo collection in the front-end. In this case, if you had omitted the query parameters, you would end up with unpredictable results. See more in this excellent explanation. Note also that while iron:router supports doing subscriptions in the route handlers, it's encouraged to handle them in the template lifecycle methods.

For your second question, the reason the Posts collection appears empty is that the code in the top level of the file is run immediately as the file is loaded. At this point, the collection subscription isn't loaded yet, so the front-end collection is empty. You need to check for subscription readiness in some reactive context, such as a template helper or Tracker.autorun, to ensure that the data is loaded:

var handle = Meteor.subscribe('posts');
if (handle.ready()) {
  // This logs the actual count of posts
  console.log(Posts.find().count());
} else {
  // This logs 0 (unless there are other subscriptions to this collection)
  console.log(Posts.find().count());
}