Angularjs wrong $index after order By

我刚接触 Angular.js,在对数组进行排序和处理排序后的数据时遇到了一些问题。

我有一个包含项目的列表,希望通过“ Store.storeName”对它进行排序,目前为止这个方法正在起作用。但是在对数据进行排序之后,我的删除函数就不工作了。我认为这是因为排序之后 $index 是错误的,所以错误的数据被删除了。

如何解决这个问题? 在作用域中而不是在视图中对数据进行排序? 如何做到这一点?

下面是一些相关代码:

视图:

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

在我的控制器中有一个 delete 函数,它可以删除特定的数据:

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

在视图中排序之前,这样做效果很好。 如果有什么重要的东西不见了,请现在告诉我。

谢谢!

40875 次浏览

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

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.

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.

I would've just left a comment, but I don't have the "reputation".

mile's solution is exactly what I needed too. To answer pkozlowski.opensource's question: when you have either nested ngRepeats, a dynamic list (e.g. where you allow removals), or both (which is my case), using $index does not work because it will be the wrong index for the back-end data after sorting and using ngInit to cache the value does not work either because it's not re-evaluated when the list changes.

Note that mile's solution allows for the attached index property name to be customized by passing a parameter <tr ng-repeat="item in items | index:'originalPosition' | orderBy:'Store.storeName'">

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

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>

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.