0

I am using AngularJS to create a page which contains a list of products that shows information such as a name, price, region etc. This is displayed kind of like an accordion with the name in the header and extra information in the body.

Since there could be a large amount of these items displayed I am using dirPagination (https://github.com/michaelbromley/angularUtils/tree/master/src/directives/pagination) to paginate these items. My markup at the moment looks like this:

<div class="custom-list" dir-paginate="asset in assets | itemsPerPage: 10 track by $index">
    <div class="custom-list-item" ng-class="{'tag-hover': isHoveredTag(asset)}">
        <div class="custom-list-item-heading" ng-click="toggleShowAssetDetails(asset)">
            <p class="col-lg-5">{{ asset.name }}</p>
            <div class="col-lg-offset-2 col-lg-3 rating">
                <rating value="asset.rating" />
            </div>
            <button ng-click="addAsset(asset)"><span class="glyphicon glyphicon-plus"></span></button>
            <div class="clearfix"></div>
        </div>
        <div class="custom-list-item-content" style="display: none" animate="shouldShowAssetDetails(asset)">
            ...
        </div>
    </div>
</div>

As you can see I'm using paginate in a pretty standard way just looping through the items in an array and displaying 10 per page. I also have a directive called rating which looks at a value called rating in the item. This is a number from 1 - 5 which is used to display a star rating system next to the name. The directive looks like this:

var rating = function ($compile) {
    return {
        restrict: "E",
        scope: {
            value: "="
        },
        link: function (scope, element, attrs) {
            scope.$watch(attrs.rating, function () {
                for (var i = 1; i <= 5; i++) {
                    if (i <= scope.value) {
                        var starElement = angular.element("<span class=\"icon icon-crown\"></span>");
                        $compile(starElement)(scope);
                        element.append(starElement);
                    } else {
                        var emptyStarElement = angular.element("<span class=\"icon-empty icon-crown\"></span>");
                        $compile(emptyStarElement)(scope);
                        element.append(emptyStarElement);
                    }
                }
            })            
        }
    }
}

This looks at the value and inserts the icons based on the value of rating (e.g if the rating was 2 the directive would insert two icon-crown icon spans and 3 icon-empty icon-crown icon spans.

This was working perfectly fine before I included the pagination. However now it will only work for the first 10 items. When you change the page, the rating will not change and just keep the same icons from the previous page, even if the values are different.

I understand this is because the directive sets everything at the beginning and it will not run when you change page because the items aren't reloading they are just being shown and hidden again. But the directive is manipulating the DOM so it doesn't update when the page changes.

The problem is I don't know how to resolve this. I thought about changing the directive to look for the pagination current page instead but then I don't have access to the current list item.

I'd appreciate any help on getting the directive to update the icons when the page is changed.

Update Here's a link to a Plunker project showing the problem I'm having: http://plnkr.co/edit/VSQ20eWCwVpaCoS7SeQq?p=preview

This is a very stripped down version of the section on my app that I'm having an issue with. There's no styling included although I have kept the CSS class structure. I've also changed the icons to use bootstrap ones just to simplify the Plunker project.

The functionality is the same however. If you go from page 1 to page 2 notice how the stars remain the same despite that fact that the asset rating values are different. However if you go to page 3 and back to page 2 or 1 they will change. The reason this happens is because there are less items on page 3 and therefore when you go back to page 1 or 2 the remaining items will be called again to retrieve the rating values.

3
  • Can you replicate it in Plunker and share the link? Doing so will greatly increase the chance of someone helping since they can play around and try different solutions. Commented Feb 12, 2015 at 10:40
  • I've included a link to Plunker in my original post Commented Feb 12, 2015 at 13:44
  • Great, will take a look at it. Commented Feb 12, 2015 at 13:46

1 Answer 1

1

You simply need to remove or replace track by $index.

Tracking by $index will give you the following behavior:

There is an array of max 10 length that represents the items to show. The first item will have index 0.

You go to the next page and the items in the array are replaced.

The first item in the array will still have index 0. Since you are tracking by index and the index has not changed, AngularJS will not recreate the DOM node representing the item. Since the DOM node isn't recreated, the directive will not execute this time and the rating will not update.

If you go from page 3 to page 2, the directive will execute for the 7 last elements on page 2, since they didn't exist on page 3, so they are considered new this time.

If you really need to use track by you should use a property that is actually related to the object (and unique), for example:

dir-paginate="asset in assets | itemsPerPage: 10 track by asset.name"

Demo: http://plnkr.co/edit/A80tSEliUkG5idBmGe3B?p=preview

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

2 Comments

Awesome thanks for that I've been trying to figure this out for hours
@tasseKATT I am using dir-paginate directive for my application. I want to know is there any way I can get current page's all records in an object? I apologies for writing up here but thought this is good way to connect.

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.