AngularJS排序后$index错误

92

我刚接触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>

在我的控制器中,我有这个删除函数,应该删除特定的数据:

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

在 View 中排序之前,这很好用。如果有重要的内容遗漏,请告诉我。

谢谢!

6个回答

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

1
@pkozlowski.opensource 你真是个天才!你可以传递一个项目,而不是索引.. 哇!!谢谢啊。 - good_evening
在Internet Explorer 8及以下版本中,Array indexOf不可用。 - Peter Hedberg
4
问题标题在询问经过orderBy排序后的错误$index,而这个答案并没有解决这个问题。有些情况下你需要正确的$index值(例如列表上的shift select)。在应用orderBy过滤器之后,我们如何获得正确的$index值? - ClearCloud8
您还可以通过以下方式从模板中的有序列表创建新数组:"ele in ordered_array = (array | filter: filter | orderBy: order_by)" - Porlune
好的!谢谢兄弟。 - jofftiquez

23

我开始学习 Angular 并遇到了类似的问题,基于@pkozlowski-opensource的回答,我只需简单地使用以下代码就解决了它:

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

1
这非常好,而且比创建自定义过滤器等更简单。 +1 - Rafique Mohammed
1
这怎么不是正确的答案呢?这解决了我的问题。 - Cyrus Zei

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);将无法按预期工作。 - martin
1
@martin,你应该用实际数字来支持有关性能的论点。过滤器的巨大劣势在于它在每个$digest循环中都会执行,因此我认为它对性能没有帮助...... - pkozlowski.opensource
@pkozlowski.opensource 这是真的,而且它在每个 $digest 循环中运行两次。重要的是“取决于可以附加哪些事件”,当您无法控制速率时,例如未经节流的滚动事件-这是一个极端情况,性能很重要。 - martin
@mile,我确实有重复项,这正是我要找的,只是有点遗憾的是 Angular 没有在 $ 变量中跟踪原始索引。我尝试了 (key, item) in items,但它也不起作用(key 不保留为原始值)。 - 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);

}

如果你使用了“orderBy”指令,就不要在“ng-repeat”指令中传递索引。你可以在我的博客中找到详细的解释。


2

如果有人需要使用$index,可以为排序/过滤后的数组命名:

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

请查看我在这里的答案。


我认为这个答案还可以,但缺少详细说明。我认为hmk的意思是一旦上面已经设置了过滤列表,就可以使用索引来检索所需的条目(即“sortedItems[$index]”)。 - Jeremythuff

1

我本想留下评论,但是我没有足够的“声望”。

mile的解决方案正是我所需要的。回答pkozlowski.opensource的问题:当你有嵌套的ngRepeat、动态列表(例如允许删除的列表)或两者兼而有之(这是我的情况),使用$index是行不通的,因为在排序后,它将成为后端数据的错误索引,并且使用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 );
    });
})

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接