Angularjs błędnie indeks $ po orderBy

92

Jestem nowy w Angular.js i mam problemy z sortowaniem tablicy i pracą z posortowanymi danymi.

Mam listę z przedmiotami i chcę posortować ją według „Store.storeName”, który do tej pory działa. Ale po posortowaniu danych moja funkcja kasowania już nie działa. Myślę, że to dlatego, że indeks $ jest nieprawidłowy po sortowaniu, a więc złe dane są usuwane.

Jak mogę to rozwiązać? Porządkujesz dane w zakresie, a nie w widoku? Jak to zrobić?

Oto odpowiedni kod:

W widoku:

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

A w moim kontrolerze mam tę funkcję usuwania, która powinna usunąć określone dane:

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

Działa to ładnie przed złożeniem zamówienia w widoku. Jeśli brakuje czegoś ważnego, pozwól mi teraz.

Dzięki!

FuzzBuzz
źródło

Odpowiedzi:

140

Zamiast tego lub opierając się na $index- które - jak zauważyłeś - wskaże indeks w posortowanej / przefiltrowanej tablicy, możesz przekazać sam element do swojej removeItemfunkcji:

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

i zmodyfikuj removeItemfunkcję, aby znaleźć indeks, używając indexOfmetody tablicy w następujący sposób:

$scope.removeItem = function(item){
   $scope.items.splice($scope.items.indexOf(item),1);
}
pkozlowski.opensource
źródło
1
@ pkozlowski.opensource Jesteś geniuszem! Możesz przekazać element, a nie indeks .. Wow !! Dzięki stary.
good_evening
Array indexOf nie jest dostępne w przeglądarce Internet Explorer 8 i starszych.
Peter Hedberg
4
Tytuł pytania dotyczy błędnego indeksu $ po orderBy, czego ta odpowiedź nie dotyczy. Istnieją przypadki, w których potrzebujesz poprawnej wartości indeksu $ (np. Shift select na liście). Jak uzyskać prawidłową wartość indeksu $ po zastosowaniu filtra orderBy?
ClearCloud8
możesz również utworzyć nową tablicę z uporządkowanej listy w szablonie, wykonując coś takiego: "ele in order_array = (array | filter: filter | orderBy: order_by)"
Porlune
Schludny! Dzięki stary.
CENT1PEDE
23

Zacząłem uczyć się angulara i stanąłem przed podobnymi problemami i na podstawie odpowiedzi @ pkozlowski-opensource rozwiązałem to po prostu czymś w rodzaju

<a>
  <img src="img/delete.png" ng-click="removeItem(items.indexOf(item))">
  {{items.indexOf(item)}}
</a> 
ad_nm
źródło
1
To jest najlepsze i takie proste, zamiast tworzyć niestandardowy filtr itp. +1
Rafique Mohammed
1
dlaczego to nie jest prawidłowa odpowiedź? To rozwiązało mój problem
Cyrus Zei
19

Miałem ten sam problem i inne odpowiedzi w tym temacie nie pasują do mojej sytuacji.

Rozwiązałem swój problem z niestandardowym filtrem:

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

które można wykorzystać w ten sposób:

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

a następnie w HTML możesz użyć item.indexzamiast $index.

Ta metoda jest odpowiednia dla kolekcji obiektów.

Proszę wziąć pod uwagę, że ten niestandardowy filtr powinien być pierwszym na liście wszystkich zastosowanych filtrów (orderBy itp.) I doda dodatkową właściwość index(nazwę można dostosować) do każdego obiektu kolekcji.

Mila
źródło
Czy możesz wyjaśnić, dlaczego inne odpowiedzi nie są odpowiednie w Twojej sytuacji?
pkozlowski.opensource
1
@ pkozlowski.opensource To jest znacznie czystsze. Również w zależności od tego, jakie zdarzenia mogą być dołączone, i złożoności elementów w indexOf o wiele bardziej wydajne. Nie $scope.items.splice($scope.items.indexOf(item),1);będzie również działać zgodnie z oczekiwaniami w przypadku zduplikowanych elementów.
martin
1
@martin powinieneś poprzeć swoje twierdzenia dotyczące wydajności danymi rzeczywistymi. Filtr ma ogromną wadę, ponieważ jest wykonywany w każdym cyklu podsumowania $, więc nie sądzę, aby to
pomagało w
@ pkozlowski.opensource To prawda i działa dwa razy na każdy cykl podsumowania $. Ważne jest to, że „w zależności od tego, jakie zdarzenia mogą być dołączone”, wydajność ma znaczenie, gdy nie masz kontroli nad szybkością, np. Zdarzenie przewijania bez ograniczenia przepustowości - skrajny przypadek, który znam.
martin
@mile Cóż, mam duplikaty i właśnie tego szukałem, tylko trochę smutne, że Angular nie śledzi ani oryginalnego indeksu w zmiennej $. próbowałem (key, item) in itemsi to też nie działa. (klucz nie jest zachowany do oryginału)
Rouche
4

Spróbuj tego:

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

}

Pełne wyjaśnienie znajdziesz w tym wpisie na moim blogu.

Dmitrij Duszkin
źródło
2

W przypadku, gdy ktoś potrzebuje użyć $index, możesz nadać nazwę posortowanej / przefiltrowanej tablicy:

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

Zobacz moją odpowiedź tutaj .

hmk
źródło
Myślę, że ta odpowiedź jest w porządku, jeśli brakuje jej szczegółów. Myślę, że hmk oznacza to, że po odłożeniu przefiltrowanej listy na bok, jak powyżej, można użyć indeksu (tj. „SortItems [$ index]”) w celu pobrania żądanego wpisu.
Jeremythuff,
1

Zostawiłbym tylko komentarz, ale nie mam „reputacji”.

rozwiązanie mile jest dokładnie tym, czego potrzebowałem. Aby odpowiedzieć na pytanie pkozlowski.opensource: jeśli masz zagnieżdżone ngRepeats, listę dynamiczną (np. Tam, gdzie zezwalasz na usuwanie) lub oba (co jest moim przypadkiem), użycie $indexnie działa, ponieważ będzie to zły indeks dla zaplecza dane po sortowaniu i użyciu ngInitdo buforowania wartości również nie działają, ponieważ nie są ponownie oceniane po zmianie listy.

Należy pamiętać, że rozwiązanie mile umożliwia dostosowanie nazwy właściwości dołączonego indeksu przez przekazanie parametru <tr ng-repeat="item in items | index:'originalPosition' | orderBy:'Store.storeName'">

Moja ulepszona wersja:

.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
źródło