Imagine you have multiple collections you want displayed on a single social news feed, for example Posts
and Users
(new signups). How would you rea
I came across this problem and I was wondering if a mixed solution would be better (or at least more efficient):
The solution proposed by David works great but, as he says, problems could arise when dealing with big collections.
What about keeping a local collection (just in the client), merge both collections into that local one when template is created and then register an observer (or maybe an observer for each remote collection) to keep the local collection up to date?
Idea 1 seems to work out of the box. I created a new meteor project and change it as follows:
test.js:
Users = new Meteor.Collection("users");
Posts = new Meteor.Collection("posts");
if (Meteor.isClient) {
Template.hello.array = function () {
var a = Users.find().fetch()
.concat(Posts.find().fetch());
return _.sortBy(a, function(entry) { return entry.votes; });
};
}
if (Meteor.isServer) {
Meteor.startup(function () {
// code to run on server at startup
Users.insert({name: "user1", votes: 1});
Users.insert({name: "user2", votes: 4});
Users.insert({name: "user3", votes: 8});
Users.insert({name: "user4", votes: 16});
Posts.insert({name: "post1", votes: 2});
Posts.insert({name: "post2", votes: 4});
Posts.insert({name: "post3", votes: 6});
Posts.insert({name: "post4", votes: 8});
});
}
test.html:
<head>
<title>test</title>
</head>
<body>
{{> hello}}
</body>
<template name="hello">
{{#each array}}
<div>{{votes}} {{name}}</div>
{{/each}}
</template>
This gives the expected list:
1 user1
2 post1
4 user2
4 post2
6 post3
8 user3
8 post4
16 user4
Then I did Users.insert({name: "new", votes: 5}
in the console and got (reactively):
1 user1
2 post1
4 user2
4 post2
5 new
6 post3
8 user3
8 post4
16 user4
By far the easiest solution is to merge them on the client in a template helper. For example:
Template.dashboard.helpers({
peopleAndPosts: function() {
var people = People.find().fetch();
var posts = Posts.find().fetch();
var docs = people.concat(posts);
return _.sortBy(docs, function(doc) {return doc.createdAt;});
}
});
and the template (assuming people and posts have a name
):
<template name="dashboard">
<ul>
{{#each peopleAndPosts}}
<li>{{name}}</li>
{{/each}}
</ul>
</template>
This will be reactive because the helper is a reactive context so any changes to People
or Posts
will cause the returned array to be recomputed. Be aware that because you are not returning a cursor, any changes to either collection will cause the entire set to render again. This won't be a big deal if the length of the returned array is relatively short.