Nodejs Angularjs Mongoose Query inside async map - node.js

From the codes below, I can add to my database. However, when I am trying to mongoose-find to look for the database, I am not getting any value. can anybody help? I want to res.json the result.
app.post('/api/infosave', function(req,res){
async.series([function (cb){
dbinfo.remove({}, function(err,result){
if (err) throw err;
cb(null);
});
}, function (cb){
var bookNum = [];
for (var i = 0; i < req.body.numBooks; i++) {
bookNum.push(i+1)
}
async.map(bookNum, function(num, cb) {
dbinfo.create({
numBooks: num,
done: false
}, cb);
}, cb);
}, function (cb){
dbinfo.find({},function(err,result){
if (err)
res.send(err);
res.json(result);
console.log(result);
cb(null);
});
}], function(error, results) {
});
});

As I said in my comment: you're calling res.json() inside one of the series function, and that won't work. What you should do is pass the result of dbinfo.find() to the local callback:
dbinfo.find({},function(err,result){
if (err)
cb(err);
cb(null, result);
});
, and in the async.series callback, call res.json():
...
}], function(error, results) {
if (error) return res.json({ error: error });
return res.json(results[2]); // the result you want should be third element of results
});

Related

node.js For i in files render item

I want to repeat some items in my template (twig) on different places, where each repeated item responds to file names in different directories, so first I need an array with filenames.
I don't know how to affect outer scope from function. This will not work:
var files_list = {'json': [], 'js': []}
fs.readdir('./public/json', function(err, items) {
if (err) throw err;
files_list.json = items;
});
res.render('index', { js: files_list.js, jsons: files_list.json });
This works, but how to render it again to get something from another directory.
fs.readdir('./public/json', function(err, items) {
if (err) throw err;
res.render('index', { jsons: items });
});
This is a var that you does't have synchronously at hand; So you should treat it the asynchronous way:
Use a callback function:
var getFiles = function(dir, cb) {
fs.readdir(dir, function(err, files) {
if (err) cb(err, null);
cb(null, files);
});
};
Usage:
getFiles('./public/json', function(err, files) {
if (err) throw err;
res.render('index', { jsons: files });
});
Or a promise:
var getFiles = function(dir) {
return new Promise(function(resolve, reject) {
fs.readdir(dir, function(err, files) {
if (err) reject(err);
resolve(files);
});
});
};
Usage:
getFiles('./public/json')
.then(function(files) {
res.render('index', { jsons: files });
}, function(err) {
throw err;
});

Callback errors in Async library

I'm using the async library together with mongoose as follows:
async.waterfall([
function(callback) {
async.map(new_tags, function(tag, callback) {
Tag.findOneAndUpdate(
{ '_id': tag._id },
{ '$setOnInsert': { '_id': tag._id, 'name': tag.name } },
{ 'upsert': true, 'new': true },
callback
);
}, callback);
}, function(tags, callback) {
for(var k = 0; k < tags.length; k++) {
res_tags.push(tags[k]._id);
}
callback(res_tags);
}
],
function(err, results) {
callback(err, results);
});
But I'm having doubts on how the catch the error at the end of async.waterfall... The code as it is will have in err, the actual resulting array (res_tags).
Can someone give me a hand?
You're not handling your callbacks appropriately. async uses error-first callbacks. This is an important concept in Node.js because this is considered the "best practice" for handling errors within a callback chain.
See this post on error-first callbacks and Node.js
See below for how to properly implement the callbacks within your code:
async.waterfall([
function(callback) {
var res
async.map(new_tags, function(tag, callback) {
Tag.findOneAndUpdate(
{ '_id': tag._id },
{ '$setOnInsert': { '_id': tag._id, 'name': tag.name } },
{ 'upsert': true, 'new': true },
function (err, doc) {
// If an error occurs, pass it back to our map callback.
if (err)
return callback(err, null);
// If there was no error return the doc
return callback(null, doc);
}
);
}, function (err, docs) {
// If an error occurred during map return it back to the waterfall
if (err)
return callback(err, null);
// Return back all docs
return callback(null, docs);
});
}, function(tags, callback) {
// For each tag push them to res_tags
async.each(tags, function(tag) {
res_tags.push(tags[k]._id);
}, function(err) {
if (err)
return callback(err, null);
return callback(null, res_tags);
});
}
],
function(err, results) {
// If an error happened during any execution in waterfall catch it and handle it
if (err)
// Error handling
else
return results; // No error, return our results
});
The first parameter of each function callback in the waterfall should be an Error object or null if there were no errors.
callback(res_tags);
Should be changed to:
callback(null, res_tags);
From the documentation (https://github.com/caolan/async#waterfall):
async.waterfall([
function(callback) {
callback(null, 'one', 'two');
},
function(arg1, arg2, callback) {
// arg1 now equals 'one' and arg2 now equals 'two'
callback(null, 'three');
},
function(arg1, callback) {
// arg1 now equals 'three'
callback(null, 'done');
}
], function (err, result) {
// result now equals 'done'
});

How to assign query results to an object

I am trying to transfer results data from query function to an object.
console.log(results) line returns 'undefined' result. What should I do?
module.exports = {
show: function(req, res) {
var results;
User.native(function(err, User) {
if(err) {
console.log("There is no exist a User by _id");
}
User.findOne({'_id' : req.param('id')},
function(err, user) {
results = user;
});
});
console.log(results);
return res.view({ stuff : results });
}
};
You have an async issue, the callback from findOne isn't necessarily executed in line with the rest of the code, so you get to the console.log(results) before results = user gets called. You'd want to change it to something like this:
show: function(req, res) {
var results;
User.native(function(err, User) {
if(err) {
console.log("There is no exist a User by _id");
}
User.findOne({'_id' : req.param('id')},
function(err, user) {
results = user;
console.log(results);
// Send response or make a callback here
});
});
}

Node.js : res.redirect("back") got Error('Can\'t set headers after they are sent.');

I have a little problem on an express.js app.
Can't redirect back after several actions with callbacks.
My applications works with objects called "markers" and they are rated by "sub categories"
The goal of these actions is to merge 2 or more subcategories, move all markers from old to the new subcategoy, and finally, delete the old sub category.
Here is the code :
The action called after checked 2 or more subcategories :
exports.postMergeSubCategories = function(req, res) {
"use strict";
var data = {};
data.subCategories = req.body.subCategoriesChecked;
data.enName = req.body.subCategory.enName;
data.frName = req.body.subCategory.frName;
data.deName = req.body.subCategory.deName;
if (data.subCategories.length > 1) {
sscategoryMapper.merge(data, function(err, sscategory) {
if (err) return console.log(err);
console.log ('Sub categories merged !');
req.flash('mergeMessage', 'Merge completed!');
res.redirect("back");
});
} else { // error
console.log('Error while merge sub categories. It seems the number of sub categories checked less 2');
req.flash('mergeMessage', 'An error occured whil merge');
res.redirect("back");
}
};
The function sscategoryMapper.merge :
module.exports.merge = function(data, callback) {
async.waterfall([
function(callback){ // create new sub category
save(data, function(err, sscategory) {
if (err) return callback(err);
callback(null, sscategory._id);
});
},
function(sscategoryId, callback){ // update marker2sscategory
async.each(data.subCategories.split('|'), function(oldSscategoryId, err) { // for each subcategories, update markers2sscategory and remove the old subcategory
async.waterfall([
function(callback) { // update maker2sscategory to set the new sscategoryId to all target markers
marker2sscategoryMapper.update(sscategoryId, oldSscategoryId, function(err) {
if (err) return callback(err);
callback(null, oldSscategoryId);
});
},
function(oldSscategoryId, callback) { // delete the old sscategory
remove(oldSscategoryId, function(err) {
if (err) return callback(err);
callback();
});
}
], function(err) {
if (err) return callback(err);
callback();
});
}, function(err) {
callback();
});
callback(null, sscategoryId);
}
], function (err, result) {
if (err) return callback(err);
callback(null, result);
});
};
UPDATE
First problem here : I called 2 times the callback ... This piece of code moved to that :
}, function(err) {
callback(null, sscategoryId);
});
}
], function (err, result) {
if (err) return callback(err);
callback(null, result);
});
};
UPDATE END
The function marker2sscategoryMapper.update :
module.exports.update = function(to, from, callback) {
// update marker2sscategory with the new subcategory
dbMarker2sscategory.update({'_sscategory' : from}, {
'_sscategory' : to
}, {multi: true}, function(err) {
if (err) return callback(new Error(err));
callback(null, to);
});
}
And the function remove :
var remove = module.exports.remove = function(id, callback) {
dbSscategory.remove({'_id' : id}, function(err) {
if (err) return callback(new Error(err));
callback(null, id);
});
};
If I comment "res.redirect("back")", it will works.
Else, the error is : "Error('Can\'t set headers after they are sent.');" 2 times.
I read that one of the causes can be duplicate callbacks ... But I don't see that in my code.
Appreciate your help.

Rewrite callback function with async series

I had this function:
exports.profileImage = function(req, res) {
var id = req.params.user_id;
userProvider.get(id, function(err, user){
if (err) throw err;
userProvider.getImageById(user['image_id'], function(err, image) {
if (err) throw err;
userProvider.writeImageToDisk(image, function(err, path){
if (err) throw err;
res.sendfile(path);
});
});
});
};
I rewrite it using local variables:
exports.profileImage = function(req, res) {
var id = req.params.user_id;
var userTemp = undefined;
var imageTemp = undefined;
async.series([
userProvider.get(id, function(err, user){
if (err) throw err;
userTemp = user;
}),
userProvider.getImageById(userTemp['image_id'], function(err, image) {
if (err) throw err;
imageTemp = image;
}),
userProvider.writeImageToDisk(imageTemp, function(err, path){
if (err) throw err;
res.sendfile(path);
})
]);}
I have a parameters which userProvider.getImageById (user json object) needs , it comes from userProvider.get function, Which invoked before, I save it to local variable.
I chose async to skip callback hell but it is not working.
Error:
Cannot read property 'image_id' of undefined.
userTemp is undefined at the point where you try to look up its image_id. It will only become not undefined when the previous asynchronous function has been called.
Using async.waterfall, you should be able to simplify your code considerably to something like (the untested);
var id = req.params.user_id;
async.waterfall([
function(callback) {
userProvider.get(id, callback);
},
function(user, callback) {
userProvider.getImageById(user['image_id'], callback);
},
userProvider.writeImageToDisk,
res.sendfile
]);
That's not how async.series works.
Each function in the array has to have the following signature:
function(callback) { ...; callback(...); };
Once the code for each function is done, you need to call the callback function to signal to async that it should run the next step (docs).
So your code should look like this:
async.series([
function(callback) {
userProvider.get(id, function(err, user) {
if (err) throw err;
userTemp = user;
callback();
});
},
function(callback) {
userProvider.getImageById(userTemp['image_id'], function(err, image) {
if (err) throw err;
imageTemp = image;
callback();
});
},
function(callback) {
userProvider.writeImageToDisk(imageTemp, function(err, path){
if (err) throw err;
res.sendfile(path);
callback();
});
}
]);
Better yet, take a look at async.waterfall for a way of passing results from one async function to the next.

Resources