Can't push to array using mongoose - arrays

I have code, basically still the MEANJS boilerplate, and I added a section to the articles for commenting. My strategy for the comments was to expose a route in express, /comments/:commentId, with a very simple comment model (it has a user object, a content string, and a likes number). I extended the article model to include an array of object IDs for the comments, and when an article was loaded, my angular resources would make a call to the /comments/:commentId to retrieve the list of comments specified by the array. Following is my server code
/* below is comments.server.controller.js */
/* THIS IS NEVER GETTING CALLED */
exports.updateArticleComments = function(req, res){
Article.findById(req.comment.article).populate('user', 'displayName').exec(function(err, article){
console.log(article);
if (err) return res.json(err);
if (!article) res.json({err: 'oops!'}); //handle this ish
article.comments[article.comments.length] = req.comment._id;
article.save(function(err, article){
if (err){
console.log('error');
} else {
res.json(article);
}
});
});
};
exports.commentsByID = function(req, res, next, id) {
Comment.findById(id).populate('user', 'displayName').exec(function(err, comment) {
if (err) return next(err);
if (!comment) return next(new Error('Failed to load comment ' + id));
req.comment = comment;
next();
});
};
/* end comments.server.controller.js */
/* begin articles.server.routes.js */
'use strict';
/**
* Module dependencies.
*/
var users = require('../../app/controllers/users.server.controller'),
articles = require('../../app/controllers/articles.server.controller'),
comments = require('../../app/controllers/comments.server.controller');
module.exports = function(app) {
// Article Routes
app.route('/articles')
.get(articles.list)
.post(users.requiresLogin, articles.create);
app.route('/articles/:articleId')
.get(articles.read)
.put(users.requiresLogin, articles.hasAuthorization, articles.update)
.post(comments.createComment, comments.updateArticleComments)
.delete(users.requiresLogin, articles.hasAuthorization, articles.delete);
// Finish by binding the article middleware
app.param('articleId', articles.articleByID);
};
/* end articles.server.routes.js */
Everything, and I mean everything, works, EXCEPT exports.updateArticleComments function. I have seriously written about 5 different function, trying lodash's _extend, and many other techniques. I can't figure out why the comments array is never being filled. Does anyone have any suggestions at all?
EDIT: was requested to share createComment, so here it is
exports.createComment = function(req, res, next){
var comment = new Comment({content: req.body.content, article: req.body.articleId});
comment.user = req.user;
comment.save(function(comment, err){
if (err){
return res.jsonp(err);
} else {
req.comment = comment;
next();
}
});
};

Article.update({_id: 'VAL' }, {
$push: {
'comments' : req.comment._id }},{upsert:true},
function(err, data) { });
Have you tried the push method? I'd also be curious if the value of comment _id is coming back and not undefined.

Related

Passing value angular to the node

I'm trying to pass the form values but I'm not getting it, I've tried it in several ways.
Apparently Node.js is not recognizing the parameters passed to the url, I have no idea what it might be.
//AngularController
$scope.adicionarUsuario = function () {
$http.post("/usuario/salvar",{params:{"usuario":$scope.usuarioform}})
.success(function (data) {
delete $scope.usuarioform;
alert(data);
$scope.salvo = true;
$scope.cadastraUsuario.$setPristine();
}).error(function (data) {
$scope.erro = true;
$scope.message = "Aconteceu um problema: " + data;
});
};
//Node Server
app.post("/usuario/salvar",function (req, res){
usuario = req.params.usuario;
if(usuario.nome == null){
console.error("Ocorreu algum problema");
res.status(500).send('Acontenceu algum problema!');
}else{
MongoClient.connect(url, function (err, db) {
if (err){
console.error(err.stack);
res.status(500).send('Acontenceu algum problema!');
}else {
var collection = db.collection('usuarios');
//Salvar Usuario
var salva_usu = {nome: usuario.nome, email: usuario.email, senha: usuario.senha};
collection.insert(salva_usu, function (err, result) {
if (err){
console.error("Ocorreu algum problema");
res.status(500).send('Acontenceu algum problema!');
}else res.status(200).send("Salvo!");
});
//Fecha a conexão
db.close();
}
});
}
})
It's more common to send parameters using data in a POST, especially yours is an object rather than strings.
You can try modifying your code to use data, or send the 3 strings you need in 3 different params.
Personally I prefer the first way because your data contains a password, which doesn't look nice if it is appended in the request URL.
The request's data is available under the data attribute.
Also, it's most likely just going to be a JSON literal -- that is, a JS String. Parse it with JSON.parse()!
Simply replace:
app.post("/usuario/salvar",function (req, res) {
usuario = req.params.usuario;
// ...
By:
app.post("/usuario/salvar",function (req, res) {
usuario = JSON.parse(req.data).params.usuario;

CloudantDB & NodeJS: Query data with specific id

I just created a NodeJS cloudantDB web starter on bluemix. Then, I have a API get data from cloudantDB and get successfull but it returns all data. Please see js file:
js file:
app.get('/api/provider', function(request, response) {
console.log("Get method invoked.. ")
db = cloudant.use(dbCredentials.dbProvider);
var docList = [];
var i = 0;
db.list(function(err, body) {
if (!err) {
var len = body.rows.length;
console.log('total # of docs -> '+len);
if(len == 0) {
// error
} else {
body.rows.forEach(function(document) {
db.get(document.id, { revs_info: true }, function(err, doc) {
if (!err) {
if(doc['_attachments']) {
// todo
} else {
var responseData = createResponseDataProvider(
doc._id,
doc.provider_type,
doc.name,
doc.phone,
doc.mobile,
doc.email,
doc.logo,
doc.address
);
}
docList.push(responseData);
i++;
if(i >= len) {
response.write(JSON.stringify(docList));
console.log('ending response...');
response.end();
}
} else {
console.log(err);
}
});
});
}
} else {
console.log(err);
}
});
If I want to add parameter to API to get specific data from DB , Do we need create search index or query on cloudant, afer that call API the same : app.get('/api/provider/:id'). Please help me review and sharing. Thanks
you could get the document by id/name:
db.get(docID, function(err, data) {
// do something
});
references:
https://github.com/apache/couchdb-nano#document-functions
https://github.com/cloudant/nodejs-cloudant#api-reference
You can use a search function of Cloudant.
You need to create search index. In search index you can manage what data you want to get.
Example: https://cloudant.com/for-developers/search/
Following this code after create search index.
...
var query = {q: "id:doc.id"};
db.search('design document name', 'index name', query, function(er, result) {
if (er) {
throw er;
}
console.log(result);
});

Angular and Mongoose - Cant access some user value. Others appear fine

Im creating a comments system and im trying to add values to the view such as the text, userName, timePosted and userProfileImageURL but the only one that wont appear is the userProfileImageURL.
I think the problem is with the controller function but it could be somewhere else altogether.
/**
* Comment middleware
*/
exports.commentByID = function (req, res, next, id) {
Comment.findById(id).populate('user', 'displayName').exec(function (err, comment) {
if (err) return next(err);
if (!comment) return next(new Error('Failed to load Comment ' + id));
req.comment = comment;
next();
});
};
or Here Possibly
/**
* List of Comments
*/
exports.list = function (req, res) {
var id = req.dealId;
console.log('Log - ' + id);
Comment.find( )
.sort('-created')
.populate('user', 'displayName')
.exec(function (err, comments) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.jsonp(comments);
}
});
};
What does the 'user' and 'displayName' parameter in this function do?
Can i add the 'userProfileImageURL' also to the returned data somehow?
Im using the profileImageURL value like this. display name is showing but not the profileImageURL
<img ng-src="{{post.user.profileImageURL}}" alt="{{post.user.displayName}}" />
/**
* List of Comments
*/
exports.list = function (req, res) {
var id = req.dealId;
console.log('Log - ' + id);
Comment.find( )
.sort('-created')
.populate('user')
.exec(function (err, comments) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.jsonp(comments);
}
});
};
Just have to delete the displayName parameter and it will send the whole user object.

How to iterate an array before responding to user using async

I have a the following code that currently works
but...
I know it is not elegant and can be done much more efficiently.
What I am trying to do is take an array of emails, search if they exist in a corresponding db with a particular templateName, and for those that do not exist (i.e are 'new'), list them back on the returned page to the user. However, they end up waiting quite a while if there are a lot of emails to check.
This is the first time I'm using async and it may not actually be the best way to do this. Some of the below has been modified from what I am actually using currently to make it easier to read/follow.
Basically, from my handler, I call the following (where both emailArray and templateName are extracted from the request parameter passed in).
var newEmails = "";
async.eachSeries(emailArray, function(entry, cb) { // check each item in array (these are the potential new emails)
utils.emailAddressAndTemplateExists(entry.toString().toLowerCase(), templateName, function (err, emailExists, templateExists ) {
if (emailExists) {
if (templateExists) {
++existingCount;
} else if (emailExists && !templateExists) {
} else {
console.log('template does not exist');
}
} else {
++newCount;
newEmails = newEmails + entry + "</br>";
}
cb();
});
//cb();
}, function (err) {
if (err) { throw err; }
var content = utils.getHTMLHead() + newEmails + utils.getHTMLClose();
utils.writeHTMLPage(response, content);
});
The utils call does the following: (the writeHTMLPage simply adds the required html tags and sends back to response).
//checks for a single email address
var emailAddressExists = function(emailAddress, callback) {
if (emailAddressCollection == null) {
//console.log("it was null " + db_singleton + " " + dbName);
emailAddressCollection = db_singleton.collection(dbName);
}
emailAddressCollection.find( { "emailAddress" : emailAddress.toLowerCase() } ).toArray( function (err, docs) {
if (err) { console.err(err); }
if (docs.length == 0) {
callback(null, false, docs.EmailsSent);
} else {
doc = docs[0];
callback(null, true, doc.EmailsSent);
}
});
}
// check for email And template
var emailAddressAndTemplateExists = function (emailAddress, templateName, callback) {
emailAddressExists(emailAddress, function (err, returnVal, templates) {
if (returnVal) {
if (templates != null) {
callback (null, true, templates.hasOwnProperty(templateName)) // email exists, checking for templateName
} else {
callback (null, true, false); // email exists, no templates at all exist
}
} else {
callback (null, false, false); // email does not exist, templates must be false
}
});
}
//creates HTML formated respnse data
function writeHTMLPage(response, content) {
// if (err) { console.error(err); response.send("Error " + err); }
response.writeHead(200, {"Content-Type": "text/html"});
response.write(content);
response.end();
}
What are more elegant and efficient way to do this?
This looks like it's constructed according to how you'd normally see it. You can look into Promises with ES6 to get a better program flow:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
Basically, it lets you chain together functions. It's not too bad for what you're doing now, it might help out some, but if you have 4-5 callbacks nested together, that's when promises can be very helpful.
You'll just have to work through structuring your code differently to use Promises, but it'll make "callback hell" less of an issue.

Adding to an array asynchronously in Node.js

I'm pretty new to this type of programming and I'm having some trouble populating an array from a nested call. I'm pretty sure this needs to be done using callbacks, but I'm having trouble wrapping my brain around it. Closures must also come into play here. I tried searching the web for a similar example but didn't find much.
Here is my original code. I tried a few different approaches but didn't pull it off.
TaskSchema.statics.formatAssignee = function(assignees) {
var users = [];
assignees.forEach(function(uid) {
mongoose.model('User').findById(uid, function(err, user) {
users.push({
name: user.name.full
, id: user.id
});
});
});
return users;
}
I really like the following pattern (recursion is the most elegant solution to async loops):
TaskSchema.statics.formatAssignee = function(assignees, callback) {
var acc = []
, uids = assignees.slice()
(function next(){
if (!uids.length) return callback(null, acc);
var uid = uids.pop()
mongoose.model('User').findById(uid, function(err, user) {
if (err) return callback(err);
acc.push({
name: user.name.full
, id: user.id
});
next();
});
})();
}
Check out async, it has an async foreach loop.
Edit
Here is the foreach method from the async library
async.forEach = function (arr, iterator, callback) {
if (!arr.length) {
return callback();
}
var completed = 0;
_forEach(arr, function (x) {
iterator(x, function (err) {
if (err) {
callback(err);
callback = function () {};
}
else {
completed += 1;
if (completed === arr.length) {
callback();
}
}
});
});
};
var _forEach = function (arr, iterator) {
if (arr.forEach) {
return arr.forEach(iterator);
}
for (var i = 0; i < arr.length; i += 1) {
iterator(arr[i], i, arr);
}
};
you could do something like:
Give formatAssignee a callback.
Count down how many users you need to push onto users.
After you push the last one, invoke the callback with the parameter users.

Resources