1

I am kind of newbie in AngularJS world, and I'm running into a problem I have't been able to solve. I have a nested ng-repeat view, because I have a nested comment system, so I build the view from an object that is like this:

comments: [{
  _id: ObjectId(x),
  parent_id: ObjectId(y),
  text: 'Hello World!',
  author: {
    id: 1,
    name: Jorge
  }
  replies: [{
    _id: ...,
    parent_id: ...
    author: {
      [...]
    }
    replies: [{
      [...]
    }]
  },
  {
    [...]
  }]
}]

So, every comment have a property named hearts which are the number of likes that it has. I have socket.io implemented so this is all running in real-time. In my controller I have a listener for a new like to a comment, the problem is, how I access a specific comment from my controller to update it likes?

I have tried by generating a dynamic id in the view like this ng-attr-id="{{'hearts_' + comment._id}}" but this is just working for the parents comments, not for the replies, and I don't know why.

I have also tried to generate a function that looks for the comments and updates it, for instance this function:

    var setNewHearts = function(comments, comment) {
      for (var i = 0; i < comments.length; i++) {
        if ((String(comments[i]._id)) == (String(comment._id))) {
          comments[i].hearts = eventData.comment.hearts;
          return comments;
        } else if (comments[i].replies) {
          setNewHearts(comments[i].replies, eventData.comment);
        } else {
          //$scope.showMoreComments = true;
        }
      }
    }
    console.log(eventData.comment);
    $scope.comments = setNewHearts($scope.comments, eventData.comment);

but with this function my $scope doesn't get updated in real-time, because I don't have the path to do it, I mean for example $scope.comments[2].replies[0].replies[3].replies[1]

I hope someone could help me with this because I am going mad! I have read something about directives. Could this be my solution?

Thank you very much.

1 Answer 1

2

A recursive directive makes the most sense, but Angular causes an infinite loop with recursive directives and it's a bit ugly to workaround. You can use ng-include recursively like this: Live demo (click).

<div ng-repeat="comment in comments" ng-include="'comment.html'"></div>

<script type="text/ng-template"  id="comment.html">
  <div>
    <p>{{comment.author.name}}: {{comment.text}}</p>
    <div class="reply" ng-repeat="comment in comment.replies" ng-include="'comment.html'"></div>
  </div>
</script>

This is the sample data I used:

  $scope.comments = [
    {
      id: 1,
      text: 'Hello World!',
      author: {
        id: 1,
        name: 'Jorge'
      },
      replies: [
        {
          id: 2,
          text: 'Hey, Jorge!',
          author: {
            id: 2,
            name: 'World'
          },
          replies: [
            {
              id: 3,
              text: 'Woah!',
              author: {
                id: 1,
                name: 'Jorge'
              }
            }
          ]
        }
      ]
    }
  ];

For the updating when the socket message comes in, I would do this:

socket.on('comment-liked', function(commentId) {
  $scope.$apply(function() {
    // recursively search all objects and return object where `obj.id === commentId`
    var comment = findCommentById($scope.comments, likedCommentId);
    // increment the counter or set to 1 if there were no hearts before
    comment.hearts = comments.hearts ? ++comment.hearts : 1;
  });
});

Here's my function to get an array of matching comments. It uses lodash/underscore flatten, but you could do that manually pretty easily if you want. It's best to be using npm modules so you can easily include lodash's flatten without bulking up your code with the whole library =D

function findCommentById(comments, id) {
  return _.flatten(comments.map(function(comment) {
    if (comment.id === id) { return comment; }
    return findCommentById(comment.replies, id);
  }))[0];
}

As you can see in this demo, the comment is updated as you wish.

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

10 Comments

Yeah! No problem with that, I'm showing correctly my nested comments. Maybe I didn't explain myself correctly but the problem now I have all of those nested comments, I need to access specific ones from my controller to change some of their attributes. For example, accessing the comment with id == 3 (in your example) and changing its author, and showing it live in real-time.
As I said, maybe I'm not explaining right myself, but I have events on the controller (client-side) fired from the server-side. In the client-side listener I got the comment.id and I want to change it and show the result live.@m59 I'm upvoting your comment because It's a solution to the nested problem!!
@Jorge I would just iterate through the comments and change what you need. It's easy on principle, but a bit complex searching deeply through all those arrays/objects. I'm working on a clean solution.
Yeah! The function that I posted in the question is working ok, the problem is that I don't get my view updated. I can't even splice and push again in $scope.comments... This is kind of hard to me. Thank you very much for your help!
@Jorge oh, lol. You just need to call $scope.$apply(), I bet. You probably are using a websocket that doesn't inform Angular when something happens, just like DOM events.
|

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.