Angular.js - How to make limitTo dynamic and persistant inside a nested ng-repeat with real time socket.io data updates

1.6k Views Asked by At

So I have an app that is updating data in real time using socket.io and displaying it with Angular JS.

I have it displaying data (comments) in multiple ng-repeats which are using ‘track by’ to ensure that duplicates are ignored when the latest data is brought in. I’m also using LimitTo to only show a certain amount of comments at a time, LimitTo is dynamic and is increased when the user clicks a button.

The HTML

<div ng-controller="CommentsController" >
<!-- Comment Repeater Starts Here -->
<div ng-repeat="comment in comments | limitTo: limit track by comment.id" >
    {{ comment.comment }}

    <!-- Nested Reply Comments Start Here -->
    <div ng-repeat="reply in comment.replies | limitTo: comment.limit track by reply.id" >
        <div class="comment-text" >
            {{ reply.comment }}
        </div>
    </div>
    <a href="#" ng-click="increaseReplyLimit(comment, comment.limit)" ng-show="hasMoreComments(comment.replies, comment.limit)" >View More Replies</a>
</div>
<a href="#" ng-click="increaseLimit(limit)" ng-show="hasMoreComments(comments,limit)" >View More Comments</a>

It works perfectly fine for my first ng-repeat, because I assign LimitTo to a variable on the scope, which is unaffected when I bring in new data through Socket.io. For my nested ng-repeat, though, I am using comment.limit as the variable for the LimitTo and this gets overwritten every single time I bring in the new data through socket.io.(the new data has a default for comment.limit - i tried leaving this blank before but then nothing shows).

The Angular

app.controller('CommentsController', function ($scope,socket,$http,$location) {

// fetching the latest comments from the API location
$http.get( $url + 'comments').success(function(comments) {
    if (comments) {  
        $scope.comments = comments;
    }
});

// updating comments via socket.io
socket.on('comment.update', function (data) {
    $scope.comments = JSON.parse(data);
});

$scope.limit = 2;

$scope.hasMoreComments = function(comments, limit) {

    if (typeof comments != "undefined" && comments != "false") {

        var $commentLength = comments.length;

        if ($commentLength > limit) {
            return true;
        } 
        return false;
    }
    return false;   
}

$scope.increaseLimit = function(limit) {
    $scope.limit = $scope.limit + 2;
}

$scope.increaseReplyLimit = function(comment, limit) {
    comment.limit = parseInt(limit) + 2;
}

});

How can I prevent the current limit for each nested repeat from getting overwritten when I bring in new data from socket.io?

I already tried doing a deep merge on both the old and new data (with the idea of updating the nested limit in the new data to reflect the current nested limit). However, when i did that, Angular completely ignored the new limit and no longer enforced any limit for nested comments.

Structure of the data being brought in

[
{
    "topics": [
        {
        "id": 75,
        "topic": "test",
        "approved": 1,
        "created_at": "-0001-11-30 00:00:00",
        "updated_at": "-0001-11-30 00:00:00",
        "slug": "test",
        "blurb": null
        }
    ],
    "id": 849,
    "user_id": 80,
    "news_id": 9,
    "context": "Test News Article 1",
    "comment": "<p>test comment 4</p>",
    "origin": "Test",
    "origin_url": "http://localhost:8000/news/test-news-article-1",
    "author": "omurphy27",
    "author_url": null,
    "votes": 0,
    "created_at": "2015-06-08 22:36:53",
    "updated_at": "2015-06-08 22:36:53",
    "approved": 1,
    "slug": "test-comment-116",
    "original": 1,
    "parent_id": null,
    "username": "omurphy27",
    "limit": 2,
    "voted": false
}

]

Been banging my head against the wall for awhile with this one and any help is much appreciated.

1

There are 1 best solutions below

0
On

I ended up creating a separate array for my nested reply limits and attaching it to the scope (rather than having these limits in the data object that was being brought in and updated in realtime by socket.io )

See my updated angular code below

app.controller('CommentsController', function ($scope,socket,$http,$location) {

// fetching the latest comments from the API location
$http.get( $url + 'comments').success(function(comments) {
    if (comments) {  
        $scope.comments = comments;
    }

    // populate my separate array for reply limits
    $scope.populateLimits(comments.length, 4);
});

$scope.populateLimits = function(limitLength, limit) {

    var limits = [];
    for (var i = limitLength - 1; i >= 0; i--) {
        limits[i] = limit;
    };

    $scope.limits = limits;
}

// updating comments via socket.io
socket.on('comment.update', function (data) {

    if (data.length > $scope.comments.length) {
        var difference = data.length - $scope.comments.length; 

        // increment the limits array by however 
        // many new comments are pulled in by socket.io
        $scope.incrementLimits(difference);
    }

    $scope.comments = JSON.parse(data);
});

$scope.incrementLimits = function(difference) {

    var newLimits = $scope.limits;
    for (var i = 0; i < difference; i++) {
        newLimits.unshift(2);
    };

   $scope.limits = newLimits;
}

$scope.limit = 2;

$scope.hasMoreComments = function(comments, limit) {

    if (typeof comments != "undefined" && comments != "false") {

        var $commentLength = comments.length;

        if ($commentLength > limit) {
            return true;
        } 
        return false;
    }
    return false;   
}

$scope.increaseLimit = function(limit) {
    $scope.limit = $scope.limit + 2;
}

$scope.increaseReplyLimit = function(limit,index) {
    $scope.limits[index] = parseInt(limit) + 2;
}

});

I populate this separate limits array based on how many 'parent' comments I have and then I increment it by however many new comments are brought in. Right now I'm just showing comments based in order, with the newest shown first, so I don't have to track which 'limits' belong to which comment; I can simply add new ones to the beginning of the array.

As for accessing the $scope.limits array in my nested ng-repeat and using it as the LimitTo variable, I do so using the following variable: limits[$index].

See my updated markup below:

<div ng-controller="CommentsController" >
<!-- Comment Repeater Starts Here -->
<div ng-repeat="comment in comments | limitTo: limit track by comment.id" >
    {{ comment.comment }}

    <!-- Nested Reply Comments Start Here -->
    <div ng-repeat="reply in comment.replies | limitTo: limits[$index] track by reply.id" >
        <div class="comment-text" >
            {{ reply.comment }}
        </div>
    </div>
    <a href="#" ng-click="increaseReplyLimit(limits[$index], $index)" ng-show="hasMoreComments(comment.replies, limits[$index])" >View More Replies</a>
</div>
<a href="#" ng-click="increaseLimit(limit)"    ng-show="hasMoreComments(comments,limit)" >View More Comments</a>
</div>

The above fixes the issue I was having where the dynamic variable I was using for the limitTo for my nested ng-repeat kept getting overwritten. I do need to have the $scope.limits sync up better with my data, but I hope this helps anyone else who was encountering the same issue.

Cheers