AngularJS最佳实践: 请小心使用 ng-repeat 中的 $index

“有客户投诉,说在删除指定的某条记录时,结果删掉的却是另外一条记录!”

看起来是个很严重的BUG。 有一次我们在工作中碰到了这个问题。 要定位这个BUG非常麻烦, 因为客户也不清楚如何重现这个问题。

后来发现这个Bug是由于在 ng-repeat 中使用了 $index 引发的。下面一起来看看这个错误是如何引发的, 以及如何避免这种bug产生,然后说说我们从中得到的经验和教训。

一个简单动作(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 之外依然有效,则必须在控制器中也进行过滤,这就需要一些不是很必要的重复代码。 值得庆幸的是本文中介绍的模式可以用来避免这种情况。

GitHub版:https://github.com/cncounter/translation/blob/master/tiemao_2015/04_ng_repeat_%24index/ng_repeat_%24index.md

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

原文日期: 2014-11-10

翻译日期: 2015-01-23

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

时间: 2024-10-12 18:59:49

AngularJS最佳实践: 请小心使用 ng-repeat 中的 $index的相关文章

AngularJS 最佳实践

AngularJS 最佳实践 转载:http://www.lovelucy.info/angularjs-best-practices.html

Angularjs最佳实践一----Angularjs基础

用这个客户端发布博客貌似没有摘要,困惑中. 浏览器是如何得到网页的呢? 让我们把因特网想象成一个邮局,当你要发信给你的朋友,首先你要在一张纸上写下文字,然后信封上写你朋友的地址,把信装入信封.再交给邮局,邮箱分检员会根据邮政编码和地址来寻找你朋友住的位置.如果他住在一个很大的社区,邮局服务人员会将信给前台由社区人员按照公寓分类. 因特网以类似的方式工作,只不过不再是一个个由街道连接的房子或公寓,而是通过路由和线缆连接的一群计算机,每台计算都有一个唯一的地址来告诉网络如何到它那儿. 就如同很多栋公

《深入理解Java虚拟机:JVM高级属性与最佳实践》读书笔记(更新中)

第一章:走进Java 概述 Java技术体系 Java发展史 Java虚拟机发展史 1996年 JDK1.0,出现Sun Classic VM HotSpot VM, 它是 Sun JDK 和 OpenJDK 中所带的虚拟机,最初并不是Sun开发 Sun Mobile- Embedded VM/ Meta- Circular VM BEA JRockit/ IBM J9 VM JRockit曾号称世界上最快的java虚拟机,BEA公司发布.J9属于IBM主要扶持的虚拟机 Azul VM/ BEA

AngularJs最佳实践三----模块

在前面的代码里,我们将功能代码放在全局的命名空间里可不是个好主意,这可能会引起很难调试的冲突还会浪费宝贵的开发时间.你可能已经猜到了,有效的用于最终产品的代码会是封装我们的功能到一个单元这个概念被称为module(模块),模块是定义AngularJs应用的主要方式.app的模块里我们将包含所有的成许代码.一个应用keyi包含几个模块,每一个模块会包含特定功能的代码.使用模块带来的好处显而易见: 保持全局命名空间干净 测试更容易编写 程序之间容易共享代码 以任意顺序载入代码的不同部分 模块的声明格

AngularJs最佳实践二----数据绑定

在我们传统的web框架中,如Rails,Controller混合了来自model的数据并且糅合在模板里成为一个view呈现给用户.这种混合风格会导致single-way-view.视图只能反映在视图呈现的时候的model数据.当然也有写框架有望实现视图和模型的自动数据绑定. AngularJs 采用了一种不同方式,即不是将数据混入模板然后替换Dom元素.AngularJs创建live模板作为视图.视图的独立组件被动态内插值替换,这个特征可能是AngularJs最重要的一个使得我们不用写一行Jav

JSP 最佳实践: 用 jsp:include 控制动态内容

在新的 JSP 最佳实践系列的前一篇文章中,您了解了如何使用 JSP include 伪指令将诸如页眉.页脚和导航组件之类的静态内容包含到 Web 页面中.和服务器端包含一样,JSP include 伪指令允许某个页面从另一个页面提取内容或数据.清单 1 重温了 include 伪指令. 清单 1. JSP include 伪指令 <![CDATA[ <%@ page language="java" contentType="text/html" %&g

Dependency injection in .NET Core的最佳实践

我们知道依赖注入(DI)是一种实现对象及其协作者或依赖关系之间松散耦合的技术. ASP.NET Core包含一个简单的内建容器来支持构造器注入. 我们试图将DI的最佳实践带到.NET Core应用程序中,这表现在以下方面: 构造器注入 注册组件 DI in testing 构造器注入 我们可以通过方法注入.属性注入.构造器注入的方式来注入具体的实例,一般来说构造器注入的方式被认为是最好的方式,所以在应用程序中将使用构造器注入,请避免使用别的注入方式.一个构造器注入的例子如: public cla

彼之蜜糖,吾之砒霜——聊聊软件开发中的最佳实践

"描述一个事物,唯有一个名词定义它的概念,唯有一个动词揭露它的行为,唯有一个形容词表现它的特征.要做的,就是用心去寻找那个名词.那个动词.那个形容词--" -- 福楼拜 (Gustave Flaubert) 我想讲个故事. 很久很久以前(一般讲故事都是这样开头吧), 两个老工程师在一起聊天,谈各自生涯中最自豪的工程.其中一个先讲述了他的杰作: " 我们建造的桥,横跨一个峡谷,峡谷很宽很深.我们花了两年时间研究地质,选择材料.聘请了最好的工程师团队来设计方案,而这又花了五年时间

.NetCore 2.1中的HttpClientFactory最佳实践

原文:.NetCore 2.1中的HttpClientFactory最佳实践 .NET Core 2.1中的HttpClientFactory最佳实践 ASP.NET Core 2.1中出现一个新的HttpClientFactory功能, 它有助于解决开发人员在使用HttpClient实例从其应用程序发出外部Web请求时可能遇到的一些常见问题. 介绍 在.NETCore平台的2.1新增了HttpClientFactory,虽然HttpClient这个类实现了disposable,但使用它的时候用