1. ホーム
  2. angularjs

AngularJSのベストプラクティス。ng-repeatの$indexに注意する。

2022-02-18 06:34:48
"指定したレコードを削除すると、代わりに別のレコードが削除されるとの苦情がありました!"
これは深刻なバグのようです。一度、仕事でこの問題にぶつかったことがあります。お客様が問題の再現方法を把握していなかったため、バグの場所を特定するのに苦労しました。
すると、バグの原因はng-repeatで$indexを使用していることにあることがわかりました。ここでは、バグの原因と回避方法、そしてそこから学んだことを紹介します。
簡単な動作の一覧
まずは、完全で有効なng-repeatの例から見ていきましょう。
<ul ng-controller="ListCtrl">
  <li ng-repeat="item in items">
    {
{item.name}}
    <button ng-click="remove($index)">remove</button>
  </li>
</ul>

対応するコントローラは以下の通りです.
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);
  };
}]);

問題なさそうでしょう?このコードも特に注目すべき点はありません。
フィルタを追加して、そして、ちょっとした変更を加えてみましょう。これは、ユーザーが検索を行えるようにするためなど、リストが長い場合によくあることです。

便宜上、searchFilter を使ってリストにレコードを問い合わせるとします。

<ul ng-controller="ListCtrl">
  <li ng-repeat="item in items | searchFilter">
    { <未定義 {item.name}}のようになります。
    <button ng-click="remove($index)">remove</button>
  </li>
</ul>
コントローラのコードはそのままです。やはり問題なさそうですよね?
実は、そこにバグが隠れているんです。言わなくても、見つけられるかな?もし見つけられたら、あなたはもうAngularの専門家です。バグは実はコントローラの中にあるのです。
$scope.remove = function(index) {
  var item = $scope.items[index];
  removeItem(item);
};

ここでは index パラメータを使用していますが、ここでバグに遭遇しました。 フィルタリングされたインデックスが、元のリストのインデックスと一致しないのです。幸い、この問題を回避する簡単な方法があります。 $index を使用する代わりに、実際のアイテムオブジェクトを使用します。
<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 関数を変更していることに注意してください。
問題と解決策をより分かりやすく説明するために、対話型の例をご覧ください。

ここから何を学ぶことができるのか?

最初の教訓は、もちろん、$indexを使うときは注意することです。特定の方法で使用するとバグが発生する可能性が高いからです。
2つ目の教訓は、このようなパターンによってより良い方法が可能になり、ある種のバグを完全に回避することができることを思い出すことです。私は、今すぐ$indexを使わないことを強くお勧めします。この単純な発想の転換によって、あなたのコードにある多くのバグを減らすことができるのです。
3つ目の教訓は、「テストは必ずしも有用ではない」ということです。自動化されたテストで十分な状況をカバーしていても、特定の入力に依存する状況のバグを見逃すことはよくあることです。バグそのものも、フィルターを使ってテストしても、必ずしも表示されるとは限りません。

4つ目の教訓は、抽象化を壊さないことです -- これは見落としがちです。理論的には、$indexはng-repeatによって作成される"テンプレート変数"です。これは、repeatブロックの内部でのみ意味を持ちます(そして、正しく機能します)。その値を外部に渡すと、コンテキストを失い、もはや有効ではなくなります。もし、本当にrepeatの外側で動作させたいのであれば、コントローラでもフィルタリングする必要があり、あまり必要のない繰り返しのコードが必要です。ありがたいことに、この記事で紹介したパターンを使えば、この状況を回避することができます。


取得元:http://blog.csdn.net/renfufei/article/details/43061877