Angularjs wrong $index after orderBy

40,663

Solution 1

Instead or relaying on the $index - which - as you have noticed - will point to the index in a sorted / filtered array, you can pass the item itself to your removeItem function:

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

and modify the removeItem function to find an index using the indexOf method of an array as follows:

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

Solution 2

I started to learn angular and faced similar trouble, and based on the answer of @pkozlowski-opensource, I solved it just with something like

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

Solution 3

I had the same problem and other answers in this topic are not suitable for my situation.

I've solved my problem with custom filter:

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

which can be used this way:

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

and then in HTML you can use item.index instead of $index.

This method is suitable for the collections of objects.

Please, take into account that this custom filter should be the first one in the list of all filters applied (orderBy etc.) and it will add the additional property index (the name is customizable) into the each object of the collection.

Solution 4

Try this:

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

}

You can find verbose explanation in this entry in my blog.

Solution 5

In case someone needs to use $index, you can give a name to the sorted / filtered array :

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

See my answer here.

Share:
40,663
FuzzBuzz
Author by

FuzzBuzz

Updated on July 25, 2020

Comments

  • FuzzBuzz
    FuzzBuzz almost 4 years

    I am new to Angular.js and have some problems sorting my array and working on that sorted data.

    I have a list with items and want so sort it by "Store.storeName", which is working so far. But after sorting the data, my delete-function is not working anymore. I think thats because the $index is wrong after sorting, and so the wrong data is deleted.

    How can I solve that? Ordering the data in the scope and not in the view? How to do that?

    Here is some relevant code:

    In the View:

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

    And in my controller I have this delete function, which should delete the specific data:

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

    This works nicely before ordering in the View. If something important is missing, please let me now.

    Thanks!

  • pkozlowski.opensource
    pkozlowski.opensource almost 10 years
    Could you elaborate on why the other answers are not suitable for your situation?
  • good_evening
    good_evening over 9 years
    @pkozlowski.opensource You are a genius! You can pass an item, not index.. Wow!! Thanks man.
  • Peter Hedberg
    Peter Hedberg over 9 years
    Array indexOf is not available in Internet Explorer 8 and lower.
  • martin
    martin over 9 years
    @pkozlowski.opensource This is far cleaner. Also depending on what events could be attached, and the complexity of the items in indexOf far far more efficient. Also $scope.items.splice($scope.items.indexOf(item),1); will not work as expected for duplicate items.
  • pkozlowski.opensource
    pkozlowski.opensource over 9 years
    @martin you should back up your claims regarding performance with real numbers. A filter has huge disadvantage of being executed on each and every $digest cycle so I don't think it helps with performance...
  • martin
    martin over 9 years
    @pkozlowski.opensource That's true, and it runs twice for each $digest cycle. The important thing is "depending on what events could be attached", performance matters when you have no control of the rate, e.g., un-throttled scroll event - extreme case I know.
  • Rafique Mohammed
    Rafique Mohammed over 8 years
    This is best and so simple rather than creating custom filter etc.. +1
  • ClearCloud8
    ClearCloud8 over 8 years
    The question title is asking about wrong $index after orderBy, which this answer does not address. There are cases where you need the correct $index value (such as shift select on a list). How do we get the correct $index value after orderBy filter has been applied?
  • Rouche
    Rouche over 8 years
    @mile Well i do have duplicates and this is what i was looking for, just a bit sad that Angular does not keep track or the original index in a $ variable. i tried (key, item) in items and it does not work either. (key is not kept to original)
  • Cyrus Zei
    Cyrus Zei almost 8 years
    how is this not the correct answer ? This solved my problem
  • Porlune
    Porlune over 7 years
    you can also create a new array from the ordered list within the template by doing something like this: "ele in ordered_array = (array | filter: filter | orderBy: order_by)"
  • jofftiquez
    jofftiquez over 7 years
    Neat! Thanks bro.
  • Jeremythuff
    Jeremythuff over 5 years
    I think this answer is fine, if lacking on detail. I think what hmk means is that once the filtered list has been set aside, as above, the index can be used against it (i.e. "sortedItems[$index]") to retrieve the desired entry.