Hmm…
//progress.js
let init = true, guessCount = 30;
let progress = new ReactiveVar(0);
Meteor.subscribe('posts-abc', /*optional: {limit: guessCount}*/);
Posts.find({/*query only what is published by posts-abc*/},{fields:{_id:1}}).observeChanges({
added: function(){
init && progress.set(Math.min(progress.curValue + 1/guessCount),1);
//use Math.min in case the number of docs was larger than our guess
}
})
progress.set(1); //in case the number of docs was smaller than our guess.
init = false;
//view.js
Tracker.autorun(()=>{
let percent = Math.round(progress.get()*100)
console.log("loaded "+percent+" percent")
})
The tracker is shorthand for any reactive function, like a blaze helper.
If the number of documents returned was smaller than our guess, you’ll see something like:
//console
"loaded 3 percent"
"loaded 7 percent"
"loaded 10 percent"
"loaded 13 percent"
"loaded 17 percent"
"loaded 100 percent"
That is satisfactory.
If the number of documents returned was larger than our guess, you’ll see something like:
//console
"loaded 3 percent"
"loaded 7 percent"
"loaded 10 percent"
"loaded 13 percent"
...
"loaded 100 percent"
//still not fully loaded. >:) we have lied to the user.
This is unacceptable.
While users like to see progress bars go from 5% to 100% suddenly (it’s oddly satisfying), no one likes to see progress bars that say 100% when the data isn’t even loaded. I know what you’re thinking, we’ll just cap it at 99% and then make it 100% when it is done loading. Please don’t do that, as it’s an even shittier UX.
The fix for this problem is to know how many documents you will be returning. One option is to somehow keep track of the length of arrays, whether it’s through publishing counts
, or keeping a cache of array/collection/collection-slice lengths on your server, which you send initially as metadata to your user when they first connect. That’s pretty tedious, tbh.
“Don’t keep track of counts, just take the length of the array…
”
- Some React person.
Here’s one way to do it. Remember, you are going to be observing the cursor anyways, so let’s take advantage of that information, instead of publishing the counts separately. In the end, the relevant count is ONLY of the cursor we’re sending to the user. The entire Posts
collection count is irrelevant if I’m sending Posts
by Mary
only.
//publish.js
Meteor.publish('posts-abc', function(args){
let query = getQueryFromArgs(args);
let cursor = Posts.find(query);
let count = cursor.count();
this.added('counts',"posts-abc", {count: count}) //only care about that initial load.
let handle = cursor.observeChanges({
added: (id, fields)=>{
this.added('posts', id, fields)
},
changed: (id, fields)=>{
this.changed('posts', id, fields)
},
removed: (id)=>{
this.removed('posts',id)
}
})
this.onStop(()=>{
handle.stop();
})
})
Effectively, the first document you send to the user will be information about how many documents you will be sending to the user – this is exactly the metadata we needed in our previous situation. Now, you can change the client like this:
//progress.js, improved
let init = true, actualCount;
let progress = new ReactiveVar(0);
Meteor.subscribe('posts');
Counts.find('posts-abc',{fields:{count:1}}).observeChanges({
added: function(id,fields){
actualCount = fields.count;
}
})
Posts.find({/*query only what is published*/},{fields:{_id:1}}).observeChanges({
added: function(){
init && progress.set(progress.curValue + 1/actualCount);
}
})
init = false;
Now, i haven’t tested any of this. It might be that some subtleties in the order-received of documents obtained over DDP ruins this solution. If your count
is sent after your posts
start getting sent, we have a problem. You can write some math on the client to fix it, though. Just keep track of the number of posts
documents sent from posts-abc
, and employ a guessCount
until you have the actualCount
, then fix the progress
variable accordingly. Check for existence of actualCount
every time you get a post. If it exists, use it, and the fixed progress bar. Else, fallback to the guessCount. I think you shouldn’t have to do this though, as you should get the count
information before the posts
information. Anyways, I warned you.