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 以外依然有效,則必須在控制器中也進行過濾,這就須要一些不是很必要的重複代碼。 值得慶幸的是本文中介紹的模式能夠用來避免這種狀況。

相關文章
相關標籤/搜索