Reputation: 560
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.
Upvotes: 1
Views: 656
Reputation: 43745
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.
Upvotes: 2