AngularJS: пользовательские директивы не работают с dirPagination

Я использую AngularJS для создания страницы, содержащей список продуктов, в котором отображается такая информация, как имя, цена, регион и т. д. Это отображается как аккордеон с именем в заголовке и дополнительной информацией в теле.

Поскольку может отображаться большое количество этих элементов, я использую dirPagination (https://github.com/michaelbromley/angularUtils/tree/master/src/directives/pagination), чтобы разбить эти элементы на страницы. Моя разметка на данный момент выглядит так:

<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>

Как видите, я использую разбиение на страницы довольно стандартным способом, просто перебирая элементы в массиве и отображая 10 на странице. У меня также есть директива под названием рейтинг, которая смотрит на значение, называемое рейтингом в элементе. Это число от 1 до 5, которое используется для отображения звездной рейтинговой системы рядом с именем. Директива выглядит так:

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);
                    }
                }
            })            
        }
    }
}

Это просматривает значение и вставляет значки на основе значения рейтинга (например, если рейтинг равен 2, директива будет вставлять два диапазона значков-короны и 3 диапазона значков-пустых значков-короны.

Это работало отлично, прежде чем я включил разбиение на страницы. Однако теперь это будет работать только для первых 10 элементов. При смене страницы рейтинг не изменится, а просто останутся те же значки с предыдущей страницы, даже если значения другие.

Я понимаю, что это потому, что директива устанавливает все в начале, и она не будет запускаться при смене страницы, потому что элементы не перезагружаются, они просто отображаются и снова скрываются. Но директива манипулирует DOM, поэтому она не обновляется при изменении страницы.

Проблема в том, что я не знаю, как это решить. Я подумал об изменении директивы для поиска текущей страницы разбиения на страницы, но тогда у меня нет доступа к текущему элементу списка.

Я был бы признателен за любую помощь в получении директивы для обновления значков при изменении страницы.

Обновление Вот ссылка на проект Plunker, показывающий мою проблему: http://plnkr.co/edit/VSQ20eWCwVpaCoS7SeQq?p=preview

Это очень урезанная версия раздела моего приложения, с которым у меня возникла проблема. Там нет стилей, хотя я сохранил структуру классов CSS. Я также изменил значки, чтобы использовать загрузочные, чтобы упростить проект Plunker.

Однако функционал тот же. Если вы перейдете со страницы 1 на страницу 2, обратите внимание, что звезды остаются прежними, несмотря на то, что значения рейтинга активов разные. Однако, если вы перейдете на страницу 3 и вернетесь на страницу 2 или 1, они изменятся. Это происходит потому, что на странице 3 меньше элементов, и поэтому, когда вы вернетесь на страницу 1 или 2, оставшиеся элементы будут вызываться снова для получения значений рейтинга.


person Serberuss    schedule 12.02.2015    source источник
comment
Можете ли вы воспроизвести это в Plunker и поделиться ссылкой? Это значительно увеличит шансы на то, что кто-то поможет, поскольку они могут поиграть и попробовать разные решения.   -  person tasseKATT    schedule 12.02.2015
comment
Я включил ссылку на Plunker в свой исходный пост.   -  person Serberuss    schedule 12.02.2015
comment
Отлично, будем смотреть.   -  person tasseKATT    schedule 12.02.2015


Ответы (1)


Вам просто нужно удалить или заменить track by $index.

Отслеживание по $index даст вам следующее поведение:

Существует массив максимальной длины 10, который представляет элементы для отображения. Первый элемент будет иметь индекс 0.

Вы переходите на следующую страницу, и элементы в массиве заменяются.

Первый элемент в массиве по-прежнему будет иметь индекс 0. Поскольку вы отслеживаете по индексу, а индекс не изменился, AngularJS не будет воссоздавать узел DOM, представляющий элемент. Поскольку узел DOM не создается заново, директива на этот раз не будет выполняться, и рейтинг не будет обновляться.

Если вы перейдете со страницы 3 на страницу 2, директива будет выполняться для 7 последних элементов на странице 2, поскольку на странице 3 их не было, поэтому на этот раз они считаются новыми.

Если вам действительно нужно использовать track by, вы должны использовать свойство, которое действительно связано с объектом (и уникально), например:

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

Демо: http://plnkr.co/edit/A80tSEliUkG5idBmGe3B?p=preview

person tasseKATT    schedule 12.02.2015
comment
Огромное спасибо за это, я пытался понять это часами - person Serberuss; 12.02.2015
comment
@tasseKATT Я использую директиву dir-paginate для своего приложения. Я хочу знать, есть ли способ получить все записи текущей страницы в объекте? Я извиняюсь за то, что пишу здесь, но подумал, что это хороший способ связаться. - person Samir Shah; 23.04.2015