发现问题
最近有客户投诉,说在删除指定的某条记录时,结果删掉的却是另外一条记录!看起来是个很严重的BUG。 有一次我们在工作中碰到了这个问题。 要定位这个BUG非常麻烦, 因为客户也不清楚如何重现这个问题。
后来发现这个Bug是由于在 ng-repeat 中使用了 $index 引发的。
一个简单动作(action)的列表
先来看看一个完整有效的ng-repeat示例。
<ul ng-controller="ListCtrl"> <li ng-repeat="item in items"> {{item.name}} <button ng-click="remove($index)">remove</button> </li></ul>
对应的控制器(controller)如下:
app.controller('ListCtrl', ['$scope', function($scope) { //items come from somewhere, from where doesn't matter for this example $scope.items = getItems(); $scope.remove = function(index) { var item = $scope.items[index]; removeItem(item); };}]);
看起来没什么问题,对吗? 这段代码也没有任何特别值得注意的。
添加一个过滤器(filter)
然后,让我们来做一个小小的修改: 给列表添加一个过滤器。 这是很常见的做法,如果列表很长的话,例如允许用户进行搜索。
为了方便起见, 假设我们通过 searchFilter 来查询列表中的记录。
<ul ng-controller="ListCtrl"> <li ng-repeat="item in items | searchFilter"> {{item.name}} <button ng-click="remove($index)">remove</button> </li></ul>
控制器的代码保持不变。 看起来仍然没有问题,是吧?
事实上,有一个bug藏在里面。 如果我不说, 你能找到吗? 如果能找到,你就已经是Angular大牛了.
请尽量不要使用 $index
BUG其实是在控制器里面:
$scope.remove = function(index) { var item = $scope.items[index]; removeItem(item);};
这里使用了 index参数, 然后就遇到了BUG: 过滤后的索引(indexs)不匹配原始列表的索引。
幸运的是,有一个很简单的方法来避免这种问题: 不要使用$index,而改成实际的item对象。
<ul ng-controller="ListCtrl"> <li ng-repeat="item in items | searchFilter"> {{item.name}} <button ng-click="remove(item)">remove</button> </li></ul>
控制器如下所示:
$scope.remove = function(item) { removeItem(item);};
注意, 这里将 remove($index)
改成 remove(item)
, 并修改了 $scope.remove
函数来直接操作传过来的对象。
这个小小的修改就完全避免了刚才的BUG。
为了更好地说明问题以及解决方案,请参考 interactive example 。
从中可以学到什么?
第一个教训当然是在使用 $index 要小心一点,因为以某些方式使用时很可能会产生BUG。
新闻热点
疑难解答
图片精选