2

I'm trying to append a bunch of comments to a MongoDB array with AngularJS's $http service, but I seem to have hit a dead end. Here is the code I tried (which crashed my DOM):

$scope.saveComment = function(i){
      console.log("id is " + i);
      $http.put('/api/its/' + i, { comments: {$push: { words: $scope.comment, userId: $scope.getCurrentUser().name } } } ).success(function(stuff){
         document.location.reload(true);
       })
    }

I was trying to use MongoDB's $push method to do this, but Angular won't have it. Any clue as to how I could do this?

Sincerely,

Peter

P.S.

Here is the server-side code for that particular model's update functionality :

// Updates an existing it in the DB.
exports.update = function(req, res) {
  if(req.body._id) { delete req.body._id; }
  It.findById(req.params.id, function (err, it) {
    if (err) { return handleError(res, err); }
    if(!it) { return res.send(404); }
    var updated = _.merge(it, req.body);
    updated.save(function (err) {
      if (err) { return handleError(res, err); }
      return res.json(200, it);
    });
  });
};`
10
  • What is your API endpoint? It appears to be your own server application, so what it is supposed to receive on that endpoint for a PUT request? Commented Jul 28, 2015 at 5:31
  • The i in the function's argument is the MongoDB _id for a particular post. So, the endpoint is therefore just an associated JavaScript object with a bunch of key/value pairs -- one of which being comments Commented Jul 28, 2015 at 5:37
  • An "EndPoint" is the URL in your request i.e /api/its. What is the server code for that method? Is it your own application or a provider API? If the latter, then who is the provider? Commented Jul 28, 2015 at 5:43
  • Yes, this is my own application. The actual url would be something like this: http://localhost:9000/api/its/:id, where :id is the i is said function's argument. Commented Jul 28, 2015 at 5:55
  • 1
    Ok, let me grab some code real quick. Commented Jul 28, 2015 at 5:59

1 Answer 1

4

There are a couple of things in here that are not great, but first to cover the basics and get you going.

The first thing is to fix the method calling the service angular side. The API endpoint certainly does not expect the MongoDB update syntax you are using, but rather just an object. So first fixing that:

$scope.saveComment = function(i){
    console.log("id is " + i);

    // Split these out so they are easy to log and debug
    var path = '/api/its' + i;

    // This must mirror the structure expected in your document for the element
    // Therefore "comments" is represented as an array of objects, even
    // where this is only one.
    var data = { 
       comments: [{ 
         words: $scope.comment,
         userId: $scope.getCurrentUser().name 
       }]
    };

    // Call service with response
    $http.put(path,data).success(function(stuff){
      document.location.reload(true);
    });
}

Now your server API end has some faults, I would prefer a total redesign, but in lack of info, just concentrating on fixing the main problems without changing much.

Assuming this is the lodash library, the .merge() function here is implemented incorrectly. It needs to be told how to "handle" the array content in the "merge" properly, and at present the best that will happen is an "overwrite". So we give it some smarts:

// Updates an existing it in the DB.
exports.update = function(req, res) {
  if(req.body._id) { delete req.body._id; }
  It.findById(req.params.id, function (err, it) {
    if (err) { return handleError(res, err); }
    if(!it) { return res.send(404); }
    var updated = _.merge(it, req.body,function(a,b) {
        if (_.isArray(a)) {
            return a.concat(b);    // join source and input
        }
    });
    updated.save(function (err) {
      if (err) { return handleError(res, err); }
      return res.json(200, updated);
    });
  });
};`

But there is a catch to that, as it will only "append" to the array. So if you put something in your input that was already there, then both the original items and anything in the array input would be added.

Dealing with that is a whole other issue to work out, depending on your needs.

From my own perpective, I would just send the array where possible and have an endpoint that is "just" for appending to the array of the document, rather than a "generic" document update as you have here.

This allows you to better utilize the MongoDB update functions, per expected actions. So something like this in the service call:

// comment can just be a singular object now
$http.put(path,{ 
    "words": "this that", 
    "userId": 123
}).success(function(stuff){

And on the server API end:

exports.addComment = function(req, res) {
  if(req.body._id) { delete req.body._id; }
  It.findByIdAndUpdate(req.params.id,
     { "$push": { "comments": req.body } },
     { "new": true },
     function(err,it) {
      if (err) { return handleError(res, err); }
      if(!it) { return res.send(404); }
      return res.json(200, it);
     }
  );
};

So that will simply take the body of a "comment" and append it to the array. Most importantly it does this "atomically", so that no other possible request could collide in doing something like the current "merge" is doing. Other requests to the same endpoint will just "append" to the array in the current state as when the request is made, and so will this.

That is what the $push operator is for, so it's wise to use it.

Some food for thought.

Sign up to request clarification or add additional context in comments.

1 Comment

Dude...don't want to sound cheesy but how did you get to be so awesome? This code is sick, dude! (for the non-believers out there, it works exactly like I envisioned~) Though I'm on a schedule right now (have a "meet and greet" coming up to show my bootcamp work so that, maybe, I can find a job! #unemployed), when I get the chance I will take a closer look at the lodash library and try to master your second offering here. Thanks, Blakes Seven!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.