Angularjs falscher $ Index nach orderBy

92

Ich bin neu in Angular.js und habe einige Probleme beim Sortieren meines Arrays und beim Bearbeiten dieser sortierten Daten.

Ich habe eine Liste mit Elementen und möchte sie nach "Store.storeName" sortieren, was bisher funktioniert. Aber nach dem Sortieren der Daten funktioniert meine Löschfunktion nicht mehr. Ich denke, das liegt daran, dass der $ -Index nach dem Sortieren falsch ist und daher die falschen Daten gelöscht werden.

Wie kann ich das lösen? Bestellen Sie die Daten im Bereich und nicht in der Ansicht? Wie geht das?

Hier ist ein relevanter Code:

In der Ansicht:

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

Und in meinem Controller habe ich diese Löschfunktion, die die spezifischen Daten löschen soll:

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

Dies funktioniert gut, bevor Sie in der Ansicht bestellen. Wenn etwas Wichtiges fehlt, lassen Sie es mich jetzt.

Vielen Dank!

FuzzBuzz
quelle

Antworten:

140

Stattdessen $indexkönnen Sie das Element selbst an Ihre removeItemFunktion übergeben, indem Sie das Element weiterleiten, das - wie Sie bemerkt haben - auf den Index in einem sortierten / gefilterten Array verweist :

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

und ändern Sie die removeItemFunktion, um einen Index mithilfe der indexOfMethode eines Arrays zu finden, wie folgt:

$scope.removeItem = function(item){
   $scope.items.splice($scope.items.indexOf(item),1);
}
pkozlowski.opensource
quelle
1
@ pkozlowski.opensource Du bist ein Genie! Sie können einen Artikel übergeben, keinen Index. Wow !! Danke, Mann.
good_evening
Array indexOf ist in Internet Explorer 8 und niedriger nicht verfügbar.
Peter Hedberg
4
Der Fragentitel fragt nach dem falschen $ index nach orderBy, den diese Antwort nicht anspricht. Es gibt Fälle, in denen Sie den richtigen $ index-Wert benötigen (z. B. Shift Select in einer Liste). Wie erhalten wir den richtigen $ index-Wert, nachdem der orderBy-Filter angewendet wurde?
ClearCloud8
Sie können auch ein neues Array aus der geordneten Liste in der Vorlage erstellen, indem Sie Folgendes tun: "ele in order_array = (array | filter: filter | orderBy: order_by)"
Porlune
Ordentlich! Dank bro.
CENT1PEDE
23

Ich fing an, eckig zu lernen und hatte ähnliche Probleme. Basierend auf der Antwort von @ pkozlowski-opensource löste ich es einfach mit so etwas wie

<a>
  <img src="img/delete.png" ng-click="removeItem(items.indexOf(item))">
  {{items.indexOf(item)}}
</a> 
ad_nm
quelle
1
Dies ist am besten und so einfach, anstatt benutzerdefinierte Filter usw. zu erstellen. +1
Rafique Mohammed
1
Wie ist das nicht die richtige Antwort? Dies löste mein Problem
Cyrus Zei
19

Ich hatte das gleiche Problem und andere Antworten in diesem Thema sind nicht für meine Situation geeignet.

Ich habe mein Problem mit dem benutzerdefinierten Filter gelöst:

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

die so verwendet werden kann:

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

und dann in HTML können Sie item.indexanstelle von verwenden $index.

Diese Methode eignet sich für die Sammlung von Objekten.

Beachten Sie bitte, dass dieser benutzerdefinierte Filter der erste in der Liste aller angewendeten Filter sein sollte (orderBy usw.) und die zusätzliche Eigenschaft index(der Name ist anpassbar) zu jedem Objekt der Sammlung hinzufügt .

Meile
quelle
Könnten Sie näher erläutern, warum die anderen Antworten für Ihre Situation nicht geeignet sind?
pkozlowski.opensource
1
@ pkozlowski.opensource Das ist viel sauberer. Auch abhängig davon, welche Ereignisse angehängt werden könnten und die Komplexität der Elemente in indexOf weitaus effizienter. Funktioniert auch $scope.items.splice($scope.items.indexOf(item),1);nicht wie erwartet für doppelte Elemente.
Martin
1
@martin sollten Sie Ihre Leistungsansprüche mit reellen Zahlen untermauern. Ein Filter hat den großen Nachteil, dass er bei jedem $ Digest-Zyklus ausgeführt wird, daher glaube ich nicht, dass er bei der Leistung hilft ...
pkozlowski.opensource
@ pkozlowski.opensource Das stimmt, und es wird zweimal für jeden $ Digest-Zyklus ausgeführt. Das Wichtigste ist, "abhängig davon, welche Ereignisse angehängt werden könnten", dass die Leistung wichtig ist, wenn Sie keine Kontrolle über die Rate haben, z. B. ein nicht gedrosseltes Bildlaufereignis - Extremfall, den ich kenne.
Martin
@mile Nun, ich habe Duplikate und das ist, wonach ich gesucht habe, nur ein bisschen traurig, dass Angular nicht den Überblick oder den ursprünglichen Index in einer $ -Variablen behält. Ich habe es versucht (key, item) in itemsund es funktioniert auch nicht. (Schlüssel wird nicht auf Original gehalten)
Rouche
4

Versuche dies:

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

}

Eine ausführliche Erklärung finden Sie in diesem Eintrag in meinem Blog.

Dimitry
quelle
2

Falls jemand etwas verwenden muss $index, können Sie dem sortierten / gefilterten Array einen Namen geben:

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

Siehe meine Antwort hier .

hmk
quelle
Ich denke, diese Antwort ist in Ordnung, wenn es an Details mangelt. Ich denke, was hmk bedeutet, ist, dass, sobald die gefilterte Liste wie oben beiseite gelegt wurde, der Index dagegen verwendet werden kann (dh "sortierte Elemente [$ index]"), um den gewünschten Eintrag abzurufen.
Jeremythuff
1

Ich hätte nur einen Kommentar hinterlassen, aber ich habe nicht den "Ruf".

Die Lösung von Mile ist genau das, was ich auch brauchte. Um die Frage von pkozlowski.opensource zu beantworten: Wenn Sie entweder ngRepeats verschachtelt haben , eine dynamische Liste (z. B. wo Sie Entfernungen zulassen) oder beides (was mein Fall ist), $indexfunktioniert die Verwendung nicht, da dies der falsche Index für das Back-End ist Daten nach dem Sortieren und Verwenden ngInitzum Zwischenspeichern des Werts funktionieren ebenfalls nicht, da sie nicht neu ausgewertet werden, wenn sich die Liste ändert.

Beachten Sie, dass mit der Mile-Lösung der Name der angehängten Indexeigenschaft durch Übergabe eines Parameters angepasst werden kann <tr ng-repeat="item in items | index:'originalPosition' | orderBy:'Store.storeName'">

Meine optimierte Version:

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