那些年,在AngularJS的路上遇到的坑

使用AngularJS这么久以来,遇到过一些坑,之前一直没有以书面的形式整理下,现在好好总结下,以供后面查备。

 

一、angular scope 在ng-if与ng-show下的区别(两者作用域的差别)

以下代码为一个针对应用列表分页的代码:

HTML代码:

对应的js代码为:

在页面中我们可以看到效果:


可以看到:在使用ng-if时,点击分页时,在ng-if包括的作用域里,$scope.currentPage的值改变了,但是ng-if所在作用域外面,打印$scope.currentPage的值时,仍然不变,始终是初始化的状态,并且可以看到获取的列表也并没有改变。

 

解决方案:将ng-if换为ng-show,或者ng-model=”currentPage”改为ng-model=”parentObj.currentPage”,下面看看解决之后的效果:

 



可以看到,选择分页时,页码相应的改变了,获得列表也正确了。

 

为什么会出现上述情况呢?小编在http://camnpr.com/javascript/1888.html和http://stackoverflow.com/questions/21869283/when-to-favor-ng-if-vs-ng-show-ng-hide??这两个地方找到了答案,究其原因就是ng-if 指令像其它指令一样,会创建一个子域(child scope)。

 

 

因此,ng-if里的ng-model的$scope是子域下的,外部作用域无法捕获$scope的更新值,而ng-show则是作用于外部作用域的。

 

综上,ng-if与ng-show的原理是不一样的:

 

1、ng-show/ng-hide是通过修改CSS样式方式控制元素显示与隐藏,对应的DOM元素会一直存在于当前页面中,而ng-if根据表达式的值动态的在当前的页面中添加删除页面元素。如果赋值表达式的值为false,那么这个元素就会从页面中删除,否则会添加一个元素。ng-if创建元素时用的是被它编译后的代码,如果ng-if内部的代码被其它方式修改过,那么修改只会对本次展现有效,页面元素重新渲染后修改效果会消失,而ng-show/ng-hide则能够保留dom元素上次修改后的状态。

 

2、作用域不同:当一个元素被ng-if从DOM中删除时,与其关联的作用域也会被销毁。而且当它重新加入DOM中时,则会生成一个新的作用域,而ng-show和ng-hide则不会。

 

二、UI闪烁问题

 

众所周知,Angular最大的亮点就是数据的双向绑定。然而,在项目的实际使用过程中,往往会碰到页面闪现出没有被解析的表达式(形如{{app.name}})。是因为在Angular初始化之前,DOM还没准备就绪,Angular正在计算并替换相应的值。

 

解决办法:

1、放弃使用{{ }}表达式,改用ng-bind指令;

 2、在元素上添加“ng-clock”,工作原理就是在初始化阶段inject了css规则,或者你可以包含这个css 隐藏规则到你自己的stylesheet。Angular就绪后就会移除这个cloak样式,让我们的应用(或者元素)立刻渲染。

 

三、ng-include指令

 

<div ng-include="views/include/footer.html"></div> 错在哪里,记得在第一次使用ng-include的时候,这样引用文件,始终在页面上都不显示footer.html的内容,后来明白ng-include需要的是一个变量,所以我们可以改写成<div ng-include="’views/include/footer.html’"></div> 或定义一个$scope变量,如:$scope.footerHtml="views/include/footer.html"; 页面引用:<div ng-include="footerHtml"></div>。效果如下图所示:

 


 

究其原因是:因为在ng-include中,是把它的参数当做变量来解释的,它会通过$eval对传入的值进行计算,然后作为模板地址去加载。

 

不过,更好的方法是把这些界面片段(partical)写成指令,那样你就不用在多处重复写路径了,更重要的是,将来你可以直接在这里扩展它的交互逻辑,从界面原型平滑的演化到线上系统。

 

下面举一个写成指令的例子:

HTML页面:页面中直接使用指令,指令会找到对应的页面进而进行DOM的渲染。

 

directive指令:

 

 

四、angularJS文件压缩问题

 

当项目上线时,我们往往需要将文件压缩以减少体积来提高页面的加载速度,普通的javascript压缩一般都没什么问题,但是如果是以下格式编码的angularJS文件压缩后,在使用则会报错:

 

 

解决办法是:使用Javascript数组方式构造控制器:把要注入的服务放到一个字符串数组(代表依赖的名字)里,数组最后一个元素是控制器的方法函数:

varBookCtrl = ['$scope', '$http', function($scope, $http) { /* constructor body */}];

 

如以下登陆的controller:


这样使用数组的方式在压缩后,就不会出现依赖的错误了。

 

 

五、IE11下get请求缓存问题,页面绑定数据不及时更新的bug

 

在项目开发的过程中,我曾经发现过一个奇葩的问题,就是在IE11浏览器下,当点击某个button触发一个函数,从而改变ng-model绑定的某个变量时,明确变量的值已经改变,但是只有在F12开启开发者调试工具时,页面才能及时更新显示改变后的变量值,一旦F12开发者调试工具关闭,变量值将不更新。但是这一现象在chrome、firefox下确不曾见,究其原因竟是IE浏览器下ajax请求缓存的问题,后来把缓存禁用后,竟恢复正常了。具体解决方法就是:在配置路由的angularJS文件中,添加如下配置代码即可:

 



 

六、angular中的unsafe问题

 

在项目中,之前遇到过这样的问题,就是当给一个ios应用上传安装包和plist文件后,一般这个ios应用的下载地址就会变为:

“itms-services:///?action=download-manifest&url=https://dn-touchc-test.qbox.me/test2.plist”这样的样子,但是当我们把这样的字符串放到<a>标签的href属性中时,其会自动在上述字符串之前加上“unsafe:”的字符串:如下图所示:

 

 

而这个这样加了“unsafe:”字样的链接被我们复制到手机浏览器里进行下载则会报找不到。

解决办法就是:在angular配置路由的配置文件中加入如下配置代码:

 

再来看结果:

 

原来angular对href是有安全检查的,只能生成它认为安全的链接。AngularJS真是为我们的安全操碎了心……而我也更加明白了{{}}并不是一个简单的模板替换,它真的把我们的html重新编译了一遍。

 

七、使用ng-repeat或ng-options进行数据循环时,track by的使用

 

在使用ng-repeat进行数据循环时,如果后端数据库采用的是moogodb的话,当 ng-repeat 的数组被替换时, 它默认并不会重新利用已有的 Dom 元素,而是直接将其全部删除并重新生成新的数组 Dom 元素,Dom 的频繁操作是非常不友好的,为什么 ng-repeat 不能利用已有的 dom 元素去更新数据呢?因为你没有把数组元素的标识属性告诉它,那么两次替换的时候它就没办法追踪了,所以当首次使用ng-repeat渲染出列表数据,再次请求渲染数据的时候,ng-repeat会往数组中每个元素加上$$hashKey属性,这个 key 是由 Angular 内部的 nextUid() 方法生成,类似数据库自增,但是是使用字符串。因此,我们使用 track by 来避免,例如:



$index表示默认的索引(自动递增),item.id是数据的主键值。

 

八、$apply的使用

 

之前在微信端修改个人设置的页面时,接口没有返回用户的相关信息,只有通过另一个接口去获取用户的具体信息,但是这个过程中发现拿到用户的具体信息重新给$scope.userData赋值后,虽然model值改变了,但是页面并没有及时更新,主要原因是angular没有检测到数据变化,所以此时需要我们手动通知angular数据改变,如下代码所示:

 这里使用的是$apply的无参数形式,另外还有一种形式是:

这样也可以,$scope.$apply() 会自动触发 $rootScope.$digest(),从而让 watchers 被触发用以更新view。具体可以参考这篇文章:深入理解Angular中的$apply()以及$digest()

 

当然,后面的路还很长,遇到的坑也不止这些,我会持续更新的……

 

 





 

posted @ 2016-07-28 16:57  zhunaoke  阅读(6091)  评论(0编辑  收藏  举报