ng-repeat 中的 track by $index

 

用ng-repeat指令遍历一个javascript数组,当数组中有重复元素的时候,angularjs会报错,这是由于ng-Repeat不容许collection中存在两个相同Id的对象。javascript

对于数字或者字符串等基本数据类型来讲,它的id就是它自身的值。所以数组中是不容许存在两个相同的数字的。为了规避这个错误,须要定义本身的track by表达式。java

// 业务上本身生成惟一的id
item in items track by item.idgit

//或者直接拿循环的索引变量$index来用
item in items track by $indexangularjs

Error: [ngRepeat:dupes]这个出错提示具体到题主的状况,意思是指数组中有2个以上的相同数字。ngRepeat不容许collection中存在两个相同Id的对象

一个简单动做(action)的列表

先来看看一个完整有效的ng-repeat示例。github

<ul ng-controller="ListCtrl">
  <li ng-repeat="item in items">
    {{item.name}}
    <button ng-click="remove($index)">remove</button>
  </li>
</ul>

对应的控制器(controller)以下:api

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)

而后,让咱们来作一个小小的修改: 给列表添加一个过滤器。 这是很常见的作法,若是列表很长的话,例如容许用户进行搜索。app

为了方便起见, 假设咱们经过 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。

第二个教训是,请记住相似这样的模式,则能够用更好的作事方式,能够彻底避免某些类型的BUG。 我强烈建议你们如今不要使用 $index, 从这种简单的思惟转变中,就能够减小代码中的不少BUG。

第三个教训是测试并非何时都有用。 即使有自动化测试,也覆盖了足够多的情形, 但对于依赖特定输入的状况,也很容易错过某些BUG。 错误自己并非每次都会出现,即便你也用过滤来测试。

第四个教训是不要破坏抽象 —— 这一点很容易被忽略。理论上 $index 是由 ng-repeat 建立的一个 “模板变量(template variable)”。 这只在 repeat 块里面有意义(并正确起做用)。 当咱们将它的值传递到外面时,它就失去了上下文从而再也不有效。 若是确实想让它在 repeat 以外依然有效,则必须在控制器中也进行过滤,这就须要一些不是很必要的重复代码。 值得庆幸的是本文中介绍的模式能够用来避免这种状况。

相关文章
相关标签/搜索