Angularjs неправильный индекс $ после orderBy

92

Я новичок в Angular.js, и у меня возникли проблемы с сортировкой моего массива и работой с отсортированными данными.

У меня есть список с элементами, и я хочу отсортировать его по "Store.storeName", который пока работает. Но после сортировки данных моя функция удаления больше не работает. Я думаю, это потому, что индекс $ неправильный после сортировки, и поэтому неправильные данные удаляются.

Как я могу это решить? Заказ данных в области действия, а не в представлении? Как это сделать?

Вот соответствующий код:

В представлении:

<tr ng-repeat="item in items | orderBy:'Store.storeName'">
                <td><input class="toggle" type="checkbox" ng-model="item.Completed"></td>
                <td>{{item.Name}}</td>
                <td>{{item.Quantity}} Stk.</td>
                <td>{{item.Price || 0 | number:2}} €</td>                
                <td>{{item.Quantity*item.Price|| 0 | number:2}} €</td>
                <td>{{item.Store.storeName}}</td> 
                <td><a><img src="img/delete.png" ng-click="removeItem($index)">{{$index}}</a></td>
            </tr>

И в моем контроллере у меня есть эта функция удаления, которая должна удалять определенные данные:

$scope.removeItem = function(index){
        $scope.items.splice(index,1);
    }

Это хорошо работает перед заказом в представлении. Если чего-то важного не хватает, позвольте мне сейчас.

Благодарность!

FuzzBuzz
источник

Ответы:

140

Вместо того, чтобы ретранслировать $index- который, как вы заметили, будет указывать на индекс в отсортированном / отфильтрованном массиве, вы можете передать сам элемент вашей removeItemфункции:

<a><img src="img/delete.png" ng-click="removeItem(item)">{{$index}}</a>

и измените removeItemфункцию, чтобы найти индекс, используя indexOfметод массива следующим образом:

$scope.removeItem = function(item){
   $scope.items.splice($scope.items.indexOf(item),1);
}
pkozlowski.opensource
источник
1
@ pkozlowski.opensource Вы гений! Вы можете передать элемент, а не индекс .. Вау !! Спасибо чувак.
good_evening
Массив indexOf недоступен в Internet Explorer 8 и ниже.
Питер Хедберг
4
В заголовке вопроса указывается неверный индекс $ index после orderBy, который не рассматривается в этом ответе. Бывают случаи, когда вам нужно правильное значение $ index (например, сдвиг выбора в списке). Как мы можем получить правильное значение $ index после применения фильтра orderBy?
ClearCloud8,
вы также можете создать новый массив из упорядоченного списка в шаблоне, выполнив что-то вроде этого: «ele in orders_array = (array | filter: filter | orderBy: order_by)»
Porlune
Аккуратно! Спасибо, бро.
CENT1PEDE
23

Я начал изучать angular и столкнулся с аналогичной проблемой, и, основываясь на ответе @ pkozlowski-opensource, я решил это просто с помощью чего-то вроде

<a>
  <img src="img/delete.png" ng-click="removeItem(items.indexOf(item))">
  {{items.indexOf(item)}}
</a> 
ad_nm
источник
1
Это лучше и так просто, чем создавать собственный фильтр и т. Д. +1
Рафик Мохаммед
1
как это не правильный ответ? Это решило мою проблему
Сайрус Зей
19

У меня была такая же проблема, и другие ответы в этой теме не подходят для моей ситуации.

Я решил свою проблему с настраиваемым фильтром:

angular.module('utils', []).filter('index', function () {
    return function (array, index) {
        if (!index)
            index = 'index';
        for (var i = 0; i < array.length; ++i) {
            array[i][index] = i;
        }
        return array;
    };
});

который можно использовать так:

<tr ng-repeat="item in items | index | orderBy:'Store.storeName'">

а затем в HTML вы можете использовать item.indexвместо $index.

Этот метод подходит для коллекций объектов.

Пожалуйста, примите во внимание, что этот настраиваемый фильтр должен быть первым в списке всех примененных фильтров (orderBy и т. Д.), И он добавит дополнительное свойство index(имя настраивается) в каждый объект коллекции.

миля
источник
Не могли бы вы пояснить, почему другие ответы не подходят для вашей ситуации?
pkozlowski.opensource
1
@ pkozlowski.opensource Это намного чище. Также в зависимости от того, какие события могут быть прикреплены, и сложность элементов в indexOf намного эффективнее. Также $scope.items.splice($scope.items.indexOf(item),1);не будет работать должным образом для повторяющихся элементов.
Мартин
1
@martin, вы должны подкрепить свои утверждения относительно производительности реальными числами. У фильтра есть огромный недостаток, заключающийся в том, что он выполняется в каждом цикле дайджеста $, поэтому я не думаю, что он помогает с производительностью ...
pkozlowski.opensource
@ pkozlowski.opensource Это правда, и он выполняется дважды для каждого цикла $ digest. Важным является то, что «в зависимости от того, какие события могут быть присоединены», производительность имеет значение, когда вы не можете контролировать скорость, например, событие нерегулируемой прокрутки - крайний случай, который я знаю.
Мартин
@mile Ну, у меня есть дубликаты, и это то, что я искал, просто немного грустно, что Angular не отслеживает исходный индекс в переменной $. Я пробовал, (key, item) in itemsи это тоже не работает. (ключ не сохранен в оригинале)
Rouche
4

Попробуй это:

$scope.remove = function(subtask) {

    var idx = $scope.subtasks.indexOf(subtask),
        st = $scope.currentTask.subtasks[idx];

    // remove from DB
    SubTask.remove({'subtaskId': subtask.id});

    // remove from local array
    $scope.subtasks.splice(idx,1);

}

Вы можете найти подробное объяснение в этой записи в моем блоге.

Дмитрий Душкин
источник
2

В случае, если кому-то нужно использовать $index, вы можете дать имя отсортированному / отфильтрованному массиву:

<tr ng-repeat="item in sortedItems = (items | orderBy:'Store.storeName') track by $index">

Смотрите мой ответ здесь .

хмк
источник
Я думаю, что этот ответ хорош, если не хватает деталей. Я думаю, что hmk означает, что после того, как отфильтрованный список был отложен, как указано выше, индекс может быть использован против него (т.е. "sortedItems [$ index]") для получения желаемой записи.
Jeremythuff
1

Я бы просто оставил комментарий, но у меня нет «репутации».

Решение mile - именно то, что мне нужно. Чтобы ответить на вопрос pkozlowski.opensource: когда у вас есть вложенные ngRepeats, динамический список (например, где вы разрешаете удаление) или и то, и другое (в моем случае), использование $indexне работает, потому что это будет неправильный индекс для серверной части data после сортировки и использования ngInitдля кеширования значения также не работают, потому что оно не переоценивается при изменении списка.

Обратите внимание, что решение mile позволяет настраивать имя присоединенного свойства индекса, передавая параметр <tr ng-repeat="item in items | index:'originalPosition' | orderBy:'Store.storeName'">

Моя измененная версия:

.filter( 'repeatIndex', function repeatIndex()
{
// This filter must be called AFTER 'filter'ing 
//  and BEFORE 'orderBy' to be useful.
    return( function( array, index_name )
    {
        index_name = index_name || 'index';
        array.forEach( function( each, i )
        {each[ index_name ] = i;});
        return( array );
    });
})
MarkMYoung
источник