Reputation: 1709
I have a list of Comments. These comments have an attribute called "vote" (users can vote comments) and they are initially sorted by votes (descending, onRender).
Right now, when users vote, the order of the comments is reactively updated to reflect the new votes.
Is it possible to somehow keep the initial sorting order intact? I would like to avoid confusing the user with comments automatically swapping order while she/he is on the page.
Is there any good way to solve this? I was thinking perhaps of a one-time sort when rendering the page, or somehow saving the order and then reapplying it whenever the collection is reactively refreshed.
Upvotes: 4
Views: 198
Reputation: 20226
You can do a non-reactive find with the reactive: false
option, ex:
Comments.find({},{sort: {vote: -1}, reactive: false});
If you want to append new comments after the original list then I would render two lists of comments, one right after the other. First render the sorted list of existing comments then reactively render the list of comments that were created after the time of initial rendering in creation order. The second list will initially be empty and will not render at all but eventually new comments will appear there with their own vote counts.
Now, since the cursor above is non-reactive, how do we go about getting the vote counts to update reactively without changing the order? Answer: (in the case of Blaze) use a reactive helper!
Template.myTemplate.helpers({
currentVote(){
return Comments.findOne(this._id).vote;
}
});
This is actually surprisingly cheap from a performance pov.
Upvotes: 2
Reputation: 7030
You could use a function to sort your Minimongo query. So something like:
const initialVotes = new Map();
Comments.find({}, {sort: (a, b) => {
if (!initialVotes.has(a._id)) initialVotes.set(a._id, a.votes);
if (!initialVotes.has(b._id)) initialVotes.set(b._id, b.votes);
return initialVotes.get(b._id) - initialVotes.get(a._id);
});
This will make it so that comments are sorted by initial votes. If anything else changes (like user edits the comments), that will reactively propagate, if a new comment is made, it will reactively be added. But if votes change, order will not change (but the vote number maybe rendered will still update).
Upvotes: 4
Reputation: 2386
since you don't care about new Comments, only new votes, i would do the initial population with a Meteor.call() sorted the way you want, as mentioned by @sdybskiy. then i would set up a subscription for those comments to get the vote count.
in the onCreated() of your template, you could set up a subscription like this:
let voteCursor = Votes.find({commentIds: comments});
voteCursor.observe({
added: function(newDocument, oldDocument) {
// the published vote would have a commentId, so here you
// would go to your client store for the comments, find the
// comment, and increment the count
},
removed: function(oldDocument) {
// same idea here, but process a vote being removed
}
});
notice i'm passing in the ids of all the comments returned from the method, so the publish would publish the votes only for those comments.
Upvotes: 1