AngularJSのベストプラクティス。ng-repeatの$indexに注意する。
2022-02-18 06:34:48
"指定したレコードを削除すると、代わりに別のレコードが削除されるとの苦情がありました!"
これは深刻なバグのようです。一度、仕事でこの問題にぶつかったことがあります。お客様が問題の再現方法を把握していなかったため、バグの場所を特定するのに苦労しました。
すると、バグの原因はng-repeatで$indexを使用していることにあることがわかりました。ここでは、バグの原因と回避方法、そしてそこから学んだことを紹介します。
簡単な動作の一覧
まずは、完全で有効なng-repeatの例から見ていきましょう。
フィルタを追加して、そして、ちょっとした変更を加えてみましょう。これは、ユーザーが検索を行えるようにするためなど、リストが長い場合によくあることです。
<li ng-repeat="item in items | searchFilter">
{ <未定義 {item.name}}のようになります。
<button ng-click="remove($index)">remove</button>
</li>
</ul>
コントローラのコードはそのままです。やはり問題なさそうですよね?
実は、そこにバグが隠れているんです。言わなくても、見つけられるかな?もし見つけられたら、あなたはもうAngularの専門家です。バグは実はコントローラの中にあるのです。
問題と解決策をより分かりやすく説明するために、対話型の例をご覧ください。
2つ目の教訓は、このようなパターンによってより良い方法が可能になり、ある種のバグを完全に回避することができることを思い出すことです。私は、今すぐ$indexを使わないことを強くお勧めします。この単純な発想の転換によって、あなたのコードにある多くのバグを減らすことができるのです。
3つ目の教訓は、「テストは必ずしも有用ではない」ということです。自動化されたテストで十分な状況をカバーしていても、特定の入力に依存する状況のバグを見逃すことはよくあることです。バグそのものも、フィルターを使ってテストしても、必ずしも表示されるとは限りません。
これは深刻なバグのようです。一度、仕事でこの問題にぶつかったことがあります。お客様が問題の再現方法を把握していなかったため、バグの場所を特定するのに苦労しました。
すると、バグの原因は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
関連
-
[解決済み】angularのonLoadとng-initの違いについて
-
[解決済み] AngularJSで$httpリクエスト中にスピナーGIFを表示する?
-
[解決済み] AngularJSのディープリンクとは何ですか?
-
[解決済み] ajaxリクエスト時にAngularjsのローディング画面が表示される。
-
[解決済み] AngularJSでkeypressイベントを使用するには?
-
[解決済み] AngularJS : ローカルストレージを使用する
-
[解決済み] Angularのui-routerでデフォルトの状態を設定する方法
-
[解決済み] controllerAs "プロパティを使用する理由は何ですか?
-
[解決済み] createspyとcreatespyobjの違いは何ですか?
-
[解決済み] AngularJSで画像を表示する
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] `ui-router` $stateParams vs. $state.params
-
[解決済み] X-Frame-Options'を'SAMEORIGIN'に設定しているため、フレーム内での表示を拒否された。
-
[解決済み] 適用がすでに進行中のエラー
-
[解決済み] select 要素のデフォルト値を設定するための ng-option の使用方法
-
[解決済み] AngularJs ReferenceError: $http is not defined
-
[解決済み] AngularJSのng-showとフェードアニメーション
-
[解決済み] Angular ng-repeatの条件付きラップアイテム(ng-repeatのグループアイテム)。
-
[解決済み] md-selectでデフォルト値を設定する方法
-
[解決済み] angularjsでチャートを作成する【終了】。
-
angularjs統合ueditor入門