I have a list of items retrieved from mongoose each with a list object referenced, but I need to somehow populate the item.list.user object associated with each list so I can use them in my template as item.list.user.username.
Item.find().populate('list').exec(function(err, items){
items.forEach(function(item){
User.findById(item.list.user), function(err, user){
item.list.user = user;
});
});
//how to get back here from User.findById() so I can render?
res.render('index', { items: items });
});
There are a few ways to go about this. The main issue is that you are assuming that the data will be populated when you render the template. That is not always the case, and you can and should always assume that any time you are doing asynchronous functions, that it won't be done unless you wait until each function call is completed.
Here is a naive way to make sure the data is available for render.
Item.find().populate('list').exec(function (err, items) {
var len = items.length
, populatedItems = [];
items.forEach(function(item, i){
User.findById(item.list.user, function (err, user) {
item.list = item.list.toObject();
item.list.user = user;
populatedItems.push(item);
if (i + 1 === len) {
res.render('index', { items: items });
}
});
});
});
Though that is not very efficient and makes unnecessary database calls. It is also harder to reason about in my opinion.
Item.find().populate('list').exec(function (err, items) {
var itemMap = {}
items.forEach(function (item, i) {
// Map the position in the array to the user id
if (!itemMap[item.list.user]) {
itemMap[item.list.user] = [];
}
itemMap[item.list.user].push(i)
item.list = item.list.toObject()
});
// Can pull an array of user ids from the itemMap object
User.find({_id: {$in: Object.keys(itemMap)}}, function (err, users) {
users.forEach(function (user) {
itemMap[user._id].forEach(function(id) {
// Assign the user object to the appropriate item
items[id].list.user = user;
})
});
res.render('index', { items: items });
});
});
After further discussion with you on IRC and troubleshooting the following is a working example for your particular case.
Item.find().populate('list').exec(function (err, items) {
var itemIds = [];
items.forEach(function (item) {
itemIds.push(item.list.user)
});
// Can pull an array of user ids from the itemMap object
User.find({_id: {$in: itemIds}}, function (err, users) {
var userMap = {}
users.forEach(function (user) {
userMap[user._id] = user
});
res.render('index', { items: items, userMap: userMap });
});
});