AngularJS实践之使用ng-repeat中$index的注意点


Posted in Javascript onDecember 22, 2016

发现问题

最近有客户投诉,说在删除指定的某条记录时,结果删掉的却是另外一条记录!看起来是个很严重的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。

      第二个教训是,请记住类似这样的模式,则可以用更好的做事方式,可以完全避免某些类型的BUG。 我强烈建议大家现在不要使用 $index, 从这种简单的思维转变中,就可以减少代码中的很多BUG。

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

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

结束语

以上就是关于AngularJS实践之ng-repeat中$index使用注意事项的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。

原文链接: AngularJS best practices: Be careful when using ng-repeat's $index

原文日期: 2014-11-10

翻译日期: 2015-01-23

翻译人员: 铁锚 http://blog.csdn.net/renfufei

Javascript 相关文章推荐
innertext , insertadjacentelement , insertadjacenthtml , insertadjacenttext 等区别
Jun 29 Javascript
精心挑选的15个jQuery下拉菜单制作教程
Jun 15 Javascript
分享一个我自己写的ToolTip提示插件(附源码)
Jan 20 Javascript
JS画线(实例代码)
Nov 20 Javascript
Chrome下ifame父窗口调用子窗口的问题示例探讨
Mar 17 Javascript
js数组中如何随机取出一个值
Jun 13 Javascript
angular.element方法汇总
Jan 07 Javascript
js实现无限级树形导航列表效果代码
Sep 23 Javascript
Angular自定义组件实现数据双向数据绑定的实例
Dec 11 Javascript
js中apply()和call()的区别与用法实例分析
Aug 14 Javascript
js实现星星海特效的示例
Sep 28 Javascript
浅谈JS的二进制家族
May 09 Javascript
JS中微信小程序自定义底部弹出框
Dec 22 #Javascript
Bootstrap源码解读表单(2)
Dec 22 #Javascript
javascript学习之json入门
Dec 22 #Javascript
Bootstrap源码解读网格系统(3)
Dec 22 #Javascript
js微信支付实现代码
Dec 22 #Javascript
jquery自定义插件结合baiduTemplate.js实现异步刷新(附源码)
Dec 22 #Javascript
Html中 IFrame的用法及注意点
Dec 22 #Javascript
You might like
vBulletin HACK----显示话题大小和打开新窗口于论坛索引页
2006/10/09 PHP
PHP has encountered an Access Violation 错误的解决方法
2010/01/17 PHP
深入PHP许愿墙模块功能分析
2013/06/25 PHP
thinkphp 一个页面使用2次分页的实现方法
2013/07/15 PHP
解决PHP4.0 和 PHP5.0类构造函数的兼容问题
2013/08/01 PHP
php检测用户是否用手机(Mobile)访问网站的类
2014/01/09 PHP
PHP变量赋值、代入给JavaScript中的变量
2015/06/29 PHP
php用正则判断是否为数字的方法
2016/03/25 PHP
PHP操作mysql数据库分表的方法
2016/06/09 PHP
JS 排序输出实现table行号自增前端动态生成的tr
2014/08/13 Javascript
jquery代码实现多选、不同分享功能
2015/07/31 Javascript
jquery实现像栅栏一样左右滑出式二级菜单效果代码
2015/08/24 Javascript
js面向对象之常见创建对象的几种方式(工厂模式、构造函数模式、原型模式)
2015/11/09 Javascript
学习JavaScript设计模式(代理模式)
2015/12/03 Javascript
js获取iframe中的window对象的实现方法
2016/05/20 Javascript
javascript作用域、作用域链(菜鸟必看)
2016/06/16 Javascript
详解PHP中pathinfo()函数导致的安全问题
2017/01/05 Javascript
Vue.js 2.0窥探之Virtual DOM到底是什么?
2017/02/10 Javascript
详谈jQuery.load()和Jsp的include的区别
2017/04/12 jQuery
详解用函数式编程对JavaScript进行断舍离
2017/09/18 Javascript
vue.js 嵌套循环、if判断、动态删除的实例
2018/03/07 Javascript
ES6中Promise的使用方法实例总结
2020/02/18 Javascript
vant自定义二级菜单操作
2020/11/02 Javascript
[00:20]DOTA2荣耀之路7:-ah fu-抢盾
2018/05/31 DOTA
python 读写中文json的实例详解
2017/10/29 Python
Python实现返回数组中第i小元素的方法示例
2017/12/04 Python
python生成器,可迭代对象,迭代器区别和联系
2018/02/04 Python
Django Rest framework之认证的实现代码
2018/12/17 Python
python+opencv实现阈值分割
2018/12/26 Python
python实现批量转换图片为黑白
2020/06/16 Python
CSS超出文本指定宽度用省略号代替和文本不换行
2016/05/05 HTML / CSS
加拿大知名的国际儿童品牌:Hatley
2016/11/09 全球购物
英国最受欢迎的价格比较网站之一:MoneySuperMarket
2018/12/19 全球购物
Java的五个基础面试题
2016/02/26 面试题
初中考试作弊检讨书
2014/02/01 职场文书
幼师辞职信范文大全
2015/05/12 职场文书