Angular 遇到的坑
Angular 遇到的坑
千里一线牵 发表于7个月前
Angular 遇到的坑
  • 发表于 7个月前
  • 阅读 14
  • 收藏 0
  • 点赞 0
  • 评论 0

华为云·免费上云实践>>>   

在Angular群里回答新手问题一段时间了,有一些Angular方面的坑留在这里备查,希望能对各位有所帮助。这个文章将来会随时更新,不会单独开新章,欢迎各位订阅。

Q1.<div ng-include="views/user/show.html"></div> 错在哪里?

如果你这么写过,会发现这个位置啥也没有加载出来,那么,错在哪里呢?错在ng-include需要的是一个变量,如果你在$scope中有这样一个变量 $scope.userShowTemplateUrl = "views/users/show.html",并且把上面这句变为<div ng-include="userShowTemplateUrl"></div>就能正常工作了。或者这样写也行:<div ng-include=" 'views/user/show.html' "></div>

原因何在?

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

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

Q2. 我的指令怎么无效?

A2. 如果你排除了代码错误等问题,那么最可能的原因是restrict。restrict参数是用来规定你可以通过哪种方式来使用指令,而这个问题之所以容易成为坑,是因为restrict的默认值是A,也就是说,默认情况下,指令只能通过属性的形式使用,比如我写了一个指令叫做appHeader,那么默认情况下我只能用这样的形式使用它:<div app-header></div>,而<app-header></app-header>的形式则是无效的。

所以,如果你用返回函数的形式使用指令,那么你就只能使用属性的方式调用它,比如:

yourModule.directive('appHeader', function() {
  return function(scope, element, attrs) {
    element.text('hello');
  }
});

如果要使用元素的方式使用指令,那么你就要这样写:

yourModule.directive('appHeader', function() {
  return {
    restrict: 'E', // 或'EA'等都可以,几种形式可以任意组合
    link: function(scope, element, attrs) {
      element.text('hello');
    }
  }
});

Q3. 修改了变量怎么界面没反应?

A3. 首先你当然要检查有没有错误以及是否确实是scope变量,如果这些都没问题,那么多半儿是$apply导致的。对于大多数操作,$apply都会自动执行,所以你不用担心,但是如果你使用了angular之外的功能,比如直接调用了setTimeout函数、挂接了jquery的事件、使用了jquery的ajax操作等等,那么系统就没有机会帮你调用$apply,界面也就没有机会刷新了,但是你如果之后又做了其他会导致$apply的操作,你会发现以前“欠下”的那次界面刷新被正常执行了了 …… 迟到的刷新仍然是bug。

典型代码如下:

setTimeout(function() {
  $scope.time = new Date()
}, 1000);

这种情况下你在页面中绑定的time变量将不会被自动刷新,无论是通过{{}}表达式,还是通过ng-*属性或者其他任何形式。怎么改呢?这样:

setTimeout(function() {
  $scope.$apply(function() {
    $scope.time = new Date();
  });
}, 1000);

不过,这不是最好的形式,最好的形式是什么呢?当然是使用angular内置的$timeout服务,它就是干这个的:

$timeout(function() {
  $scope.time = new Date();
});

没有$apply,却正常工作,没bug,而且漂亮多了吧?不过这里别忘了你得把$timeout服务进行依赖注入,不然它是undefined。

Q4. ng-click 写成 ng-class 导致的界面停止响应

A4. 这是我自己犯过的一个低级错误,属于深度依赖ide导致的问题。ide的自动代码提示功能,ng-cl的第一个候选项是ng-class,如果偷懒少打了一个字,那么本来想写ng-click的代码就会写成ng-class,结果就是,无休止的重新计算ng-class中的表达式,其中的原因还没来得及看源码研究。

如果遇到界面停止响应的问题,而且你也同样深度依赖ide,那么,从这个角度查查看吧。

Q5. 我知道你不拜金,但别忘了$

A5. 在angular中有一个通用的约定:angular的内部服务、方法、属性通常都会以$开头,而相应的,它也要求你自己的命名不要用$开头。比较容易忘记用$开头的主要是一些方法,特别是$apply, $watch, $on, $broardcast, $emit这些,而这些如果你写错了,在chrome中你将得到一个莫名其妙的提示 TypeError: undefined is not a function! 可恶的是,连函数名字都没有!所以,虽然我知道你不拜金,但是千万不要忘了写$!

Q6. 注意作用域的原型继承问题!

A6. 在Angular中,作用域是通过原型链进行继承的。而这种继承有一个问题,那就是在子类中对变量进行赋值时,不会去修改父级的。

假设scopeA继承自scopeB,而在scopeB中定义了一个变量value: 1,这时候,读取scopeA.value可以正确取到值,但是如果赋值,就有问题了 scopeA.value = 2,这时候,scopeB.value的值是多少呢?你可能以为是2,但它是1!原因就在于原型继承时对变量的赋值不会修改原型中的值,而是直接在当前scope中创建一个同名的属性。

这个现象导致了一个容易让新手困惑的问题:

下面的代码工作正常:

<label><input type="radio" ng-model="color" value="red">  Red </label><br/>
<label><input type="radio" ng-model="color" value="green"> Green</label><br/>
<label><input type="radio" ng-model="color" value="blue"> Blue </label><br/>

{{color}}

而下面的代码工作不正常,color值将不会随着选择而变化:

<div ng-repeat="value in ['red', 'green', 'blue']">
  <label><input type="radio" ng-model="color" ng-value="value">  {{value}} </label>
</div>
{{color}}

它的原因就在于color值定义在当前scope中,而ng-repeat创建了一个子scope,它使用原型继承的方式从父级继承下来。对color值的修改,会去修改子级的变量,而父级的同名变量不会被修改。

要想让它正常工作,就改成这样:

<div ng-repeat="value in ['red', 'green', 'blue']">
  <label><input type="radio" ng-model="vm.color" ng-value="value">  {{value}} </label>
</div>
{{vm.color}}

这里,把color定义成了一个vm对象的属性,这时候,因为只需要对vm的成员进行赋值,而不存在对vm进行赋值的情况,所以赋值会正确的作用于父级scope上。这里的vm只是$scope上的一个对象,叫别的名字也可以,只是因为它的实际作用是ViewModel,所以我习惯于把它命名为vm。

在Angular 1.2以后的版本中引入了controllerAs语法,可以一劳永逸的解决这个问题。具体的用法请参见 http://www.cnblogs.com/whitewolf/p/3493362.html

如果使用1.2以下的版本,可以在controller的第一句加上这样的语法来模拟:

var vm = $scope.vm = {};
所有的$scope变量都改为赋值给vm变量就可以了,view中也要相应的引用vm变量。

Q7. angular.module的两种写法:含义大不同

angular.module('name', [])angular.module('name') 虽然看起来很相似,但是!它们的含义却是截然不同的!

angular.module('name', [])是创建一个新的module,[]表示它没有依赖任何其他模块,如果已经有了一个同名模块,则会覆盖现有的。

angular.module('name')是查找一个现有module,如果这个module不存在,则返回空值。

如果把带方括号的形式(创建)误用为不带方括号的形式(引用),那么在它的返回值上调用controller等函数会出现空指针错误。

而如果把引用形式误用为创建形式,则会导致难以理解的“对象不存在”错误,但是你却明明定义过那个service或者controller等对象!这种问题就是因为后面的模块定义覆盖了以前的模块定义,你定义过的那些对象都被随着以前的module而丢掉了!

Q8. 第三方指令不起作用

最可能的原因是你没有加入模块依赖。第三方指令通常会定义在自己的模块中,所以这个模块必须被你的app模块所依赖,其中包含的指令才能在view中使用。比如你要使用ui-select2指令,就必须在自己的模块定义中加入这个依赖:

angular.module('app', [
  'ngCookies',
  'ngResource',
  'ngSanitize',
  'ui.select2'
]).....

如果仍然没有解决问题,请看看控制台中有没有错误信息,以及是否存在Q2所提的情况。

 

 这得益于 AngularJS 中的双向数据绑定特性(Two Way Data-Binding),将 Model 和 View 自动关联了起来,在更复杂的业务场景下,还有代码分离的好处,将 DOM 操作和应用逻辑解耦,非常实用。

     不过没有银弹,和其他框架一样,AngularJS 也有它的局限。CRUD 类型的操作是它所擅长的,想想看以前写过的管理后台,几乎大部分都是从数据库中读取数据,然后呈现在页面上,进行各种增删改查。AngularJS 约定了一套规范(约定优于配置),于是你可以很便捷地操作数据。而在其他方面,例如开发复杂的 Web 游戏,AngularJS 则是无用武之地了。

一、AngularJS 中的精美特性

双向绑定

上面的例子已经说明了,我们可以像 PHP Smarty 模板一样在 HTML 中写表达式,用 {{ 和 }} 包起来。在 AngularJS 里,View 和 Model 是在 Controller 里面绑定的,所以无论你在 View 的表单中修改了内容,还是在 Controller 里通过代码修改了 Model 值,两边都会即时发生变化,同步更新。因为 AngularJS 会监控 (watch) Model 对象的变化,随时反映到 View 中。

Filter

Filter 类似 Unix 里面的 | 管道概念,AngularJS 把它搬到了前端。还是举个例子,你们感受一下——

1

2

3

4

<div>{{ 9999 | number }}</div>

<div>{{ 9999+1 | number:2 }}</div>

<div>{{ 9*9 | currency }}</div>

<div>{{ 'Hello World' | uppercase }}</div>

输出结果:

1

2

3

4

9,999

10,000.00

$81.00

HELLO WORLD

  

二、AngularJS 中的一些“坑”

由于过去写 JavaScript 的习惯使然,人们很容易掉进一些 AngularJS 的陷阱里。下面的内容假设你已经了解前端 MVC 概念,并对 AngularJS 有了一定经验,初学者读起来可能比较艰深晦涩。

DOM 操作

避免使用 jQuery 来操作 DOM,包括增加元素节点,移除元素节点,获取元素内容,隐藏或显示元素。你应该使用 directives 来实现这些动作,有必要的话你还要编写自己的 directives。

如果你感到很难改变习惯,那么考虑从你的网页中移除 jQuery 吧。真的,AngularJS 中的 $http 服务非常强大,基本可以替代 jQuery 的 ajax 函数,而且 AngularJS 内嵌了 jQLite —— 它内部实现的一个 jQuery 子集,包含了常用的 jQuery DOM 操作方法,事件绑定等等。但这并不是说用了AngularJS 就不能用 jQuery 了。如果你的网页有载入 jQuery 那么 AngularJS 会优先采用你的 jQuery,否则它会 fall back 到 jQLite。

需要自己编写 directives 的情况通常是当你使用了第三方的 jQuery 插件。因为插件在 AngularJS 之外对表单值进行更改,并不能即时反应到 Model 中。例如我们用得比较多的 jQueryUI datepicker 插件,当你选中一个日期后,插件会将日期字符串填到 input 输入框中。View 改变了,却并没有更新 Model,因为$('.datepicker').datepicker(); 这段代码不属于 AngularJS 的管理范围。我们需要编写一个directive 来让 DOM 的改变即时更新到 Model 里。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

var directives = angular.module('directives', []);

  

directives.directive('datepicker'function() {

   return function(scope, element, attrs) {

       element.datepicker({

           inline: true,

           dateFormat: 'dd.mm.yy',

           onSelect: function(dateText) {

               var modelPath = $(this).attr('ng-model');

               putObject(modelPath, scope, dateText);

               scope.$apply();

           }

       });

   }

});

  然后在 HTML 中引入这个 direcitve

1

<input type="text" datepicker ng-model="myObject.myDateValue" />

  

说白了 directive 就是在 HTML 里写自定义的标签属性,达到插件的作用。这种声明式的语法扩展了 HTML。

需要说明的是,有一个 AngularUI 项目提供了大量的 directive 给我们使用,包括 Bootstrap 框架中的插件以及基于 jQuery 的其他很热门的 UI 组件。我之前说过 AngularJS 的社区很活跃嘛,生态系统健全。

ngOption 中的 value

这是个大坑。如果你去查看 ngOption 生成的 <select> 中的 <option> 的选项值(每个 <option value="xxx"> 的 value 部分),那绝对是枉费心机。因为这里的值永远都会是 AngularJS 内部元素的索引,并不是你所指定的表单选项值。

还是要转变观念,AngularJS 已经不再用表单进行数据交互了,而是用 Model。使用 $http 来提交 Model,在 php 中则使用 file_get_contents('php://input') 来获取前端提交的数据。

{{ }} 的问题

在页面初始化的时候,用户可能会看到 {{ }},然后闪烁一下才出现真正的内容。
解决办法:

  1. 使用 ng-cloak directive 来隐藏它
  2. 使用 ng-bind 替代 {{ }}

将界面与业务逻辑分离

Controller 不应该直接引用 DOM,而应该控制 view 的行为。例如“如果用户操作了 X,应该发生什么事情”,“我从哪里可以获得 X?”

Service 在大部分情况下也不应该直接引用 DOM,它应该是一个单例(singletons),独立于界面,与 view 的逻辑无关。它的角色只是“做 X 操作”。

DOM 操作应该放在 directives 里面。

尽量复用已有功能

你所写的功能很可能 AngularJS 已经实现了,有一些代码是可以抽象出来复用的,使用更 Angular 的方式。总之就是很多 jQuery 的繁琐代码可以被替代。

1. ng-repeat

ng-repeat 很有用。当 Ajax 从服务器获得数据后,我们经常使用 jQuery (比如上面讲过的例子) 向某些 HTML 容器节点中添加更多的元素,这在 AngularJS 里是不好的做法。有了 ng-repeat 一切就变得非常简单了。在你的 $scope 中定义一个数组 (model) 来保存从服务器拉取的数据,然后使用 ng-repeat 将它与 DOM 绑定即可。下面的例子初始化定义了 friends 这个 model

1

2

3

4

5

6

7

8

<div ng-init="friends = [{name:'John', age:25}, {name:'Mary', age:28}]">

    I have {{friends.length}} friends. They are:

    <ul>

        <li ng-repeat="friend in friends">

            [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.

        </li>

    </ul>

</div>

  显示结果

1

2

3

I have 2 friends. They are:

  [1] John who is 25 years old.

  [2] Mary who is 28 years old.

  

2. ng-show

ng-show 也很有用。使用 jQuery 来根据条件控制界面元素的显示隐藏,这很常见。但是 Angular 有更好的方式来做到这一点。ng-show (以及 ng-hide) 可以根据布尔表达式来决定隐藏和显示。在 $scope 中定义一个变量:

1

2

3

<div ng-show="!loggedIn">

    点击 <a href="#/login">这里</a> 登录

</div>

  

类似的内置 directives 还有 ng-disabled, ng-switch 等等,用于条件控制,语法简洁,都很强大。

3. ng-class

ng-class 用于条件性地给元素添加 class,以前我们也经常用 jQuery 来实现。Angular 中的 ng-class 当然更好用了,例子:

1

<div ng-class="{ errorClass: isError, warningClass: isWarning, okClass: !isError && !isWarning }">...</div>

  

在这里 ng-class 接受一个 object 对象,key 为 CSS class 名,值为 $scope 变量控制的条件表达式,其他类似的内置 directives 还有 ng-class-even 和 ng-class-odd,很实用。

$watch 和 $apply

AngularJS 的双向数据绑定是最令人兴奋的特性了,然而它也不是全能的魔法,在某些情况下你需要做一些小小的修正。

当你使用 ng-model, ng-repeat 等等来绑定一个元素的值时, AngularJS 为那个值创建了一个 $watch,只要这个值在 AngularJS 的范围内有任何改变,所有的地方都会同步更新。而你在写自定义的 directive 时,你需要定义你自己的 $watch 来实现这种自动同步。

有时候你在代码中改变了 model 的值,view 却没有更新,这在自定义事件绑定中经常遇到。这时你就需要手动调用 scope.$apply() 来触发界面更新。上面 datepicker 的例子已经说明了这一点。第三方插件可能会有 call back,我们也可以把回调函数写成匿名函数作为参数传入$apply()中。

将 ng-repeat 和其他 directives 结合起来

ng-repeat 很有用,不过它和 DOM 绑定了,很难在同一个元素上使用其他 directives (比如 ng-show, ng-controller 等等)。

如果你想对整个循环使用某个 directive,你可以在 repeat 外再包一层父元素把 directive 写在那儿;如果你想对循环内部的每一个元素使用某个 directive,那么把它放到 ng-repeat 的一个子节点上即可。

Scope

Scope 在 templates 模板中应该是 read-only 的,而在 controller 里应该是 write-only 的。Scope 的目的是引用 model,而不是成为 model。model 就是我们定义的 JavaScript 对象。

$rootScope 是可以用的,不过很可能被滥用

Scopes 在 AngularJS 中形成一定的层级关系,树状结构必然有一个根节点。通常我们用不到它,因为几乎每个 view 都有一个 controller 以及相对应的自己的 scope。

但偶尔有一些数据我们希望全局应用在整个 app 中,这时我们可以将数据注入 $rootScope。因为其他 scope 都会继承 root scope,所以那些注入的数据对于 ng-show 这类 directive 都是可用的,就像是在本地 $scope 中的变量一样。

当然,全局变量是邪恶的,你必须很小心地使用 $rootScope。特别是不要用于代码,而仅仅用于注入数据。如果你非常希望在 $rootScope 写一个函数,那最好把它写到 service 里,这样只有用到的时候它才会被注入,测试起来也方便些。

相反,如果一个函数的功能仅仅是存储和返回一些数据,就不要把它创建成一个 service。

三、AngularJS 项目的目录结构

怎样组织代码文件和目录?这恐怕是初学者一开始就会遇到的问题。AngularJS 应用开发的官方入门项目angular-seed,其文件结构是这样的:

 

  • css/
  • img/
  • js/
    • app.js
    • controllers.js
    • directives.js
    • filters.js
    • services.js
  • lib/
  • partials/

这种结构对于一个简单的单页 app 来说是可行的,只是一旦代码中存在多个 Controller 或者 Service,就很难找到想要寻找的对象了。我们可以对文件按照业务逻辑进行拆分,就像下面这样:

  • controllers/
    • LoginController.js
    • RegistrationController.js
    • ProductDetailController.js
    • SearchResultsController.js
  • directives.js
  • filters.js
  • models/
    • CartModel.js
    • ProductModel.js
    • SearchResultsModel.js
    • UserModel.js
  • services/
    • CartService.js
    • UserService.js
    • ProductService.js

这种结构把不同的业务功能拆分为独立的文件,条理清晰,但是仍有一定的局限性。最大的问题是一个业务功能的代码分布在controllers, models, servers 三个不同目录下,要从中挑出正确的文件,建立起代码关联,还是有些麻烦。按照功能进行模块化划分目录结构,应该要更为合理一些:

  • cart/
    • CartModel.js
    • CartService.js
  • common/
    • directives.js
    • filters.js
  • product/
    • search/
      • SearchResultsController.js
      • SearchResultsModel.js
    • ProductDetailController.js
    • ProductModel.js
    • ProductService.js
  • user/
    • LoginController.js
    • RegistrationController.js
    • UserModel.js
    • UserService.js

这样也是适合 RequireJS 等模块加载器的自然直观的代码组织方式。

 

AngularJS 是制作 SPA(单页面应用程序)和其它动态Web应用最广泛使用的框架之一。我认为程序员在使用AngularJS编码时有一个大的列表点应该记住,它会以这样或那样的方式帮助到你。下面是一些我遵守的最佳实践建议,同时也想推荐给你们。 我坚信有更多的功能也应该是这份列表的一部分,我邀请你们都来提建议或者在下面评论,从而使这个成为完整的最佳实践指南。

一、依赖注入:

1. 依赖注入是AngularJS框架最好的特性之一,我们应该经常使用它。当我们需要对我们的应用程序进行测试用例覆盖时,它将真正的起到帮助。

2. 为依赖提供别名,这样他们不会在(JS代码)压缩过程中重命名,因为在AngularJS依赖是通过命名来实现的(注:AngularJS通过控制器构造函数的参数名字来推断依赖服务名称的)。

 

1

2

3

4

5

6

angular.module(‘myApp’).controller(&#039;MyController&#039;, [&#039;$scope&#039;,  &#039;MyService&#039;,function($scope, MyService) {

 

    // controller logic

 

    }

   ]);

 

二、作用域

1. 在templates(模板)中scope(作用域)按只读对待。这就是说即使AngularJS允许我们在templates中编写代码修改scope,我们必须非常谨慎或者就不应该做。

2. 在controllers(控制器)中scope按只写对待。这就是说一个controllers负责使用另一个组件,就像一个服务,获取template 将要显示的数据和把数据写到scope的一个对象中。

  • 作为一个经验法则,我们必须总是在绑定时使用”.“。这就是说我们应该绑定到scope的对象,而不是直接的属性,否则可能会在子$scope导致意外的行为,因为$scope基于Java-script的原型继承机制。
  • 下面的代码我们可以看到,superhero是scope上一个通过Superhero服务返回的对象,同时相同的对象被用来绑定在view(视图)上。

 

XHTML

 

1

2

3

4

5

<div class="form-group">

    <label class="control-label" for="name">Super Power</label>

       <div class="controls">

      <input type="text" data-ng-model="superhero.superPower">

</div>

 

JavaScript

 

1

2

3

$scope. superhero = Superheros.get({

    superheroId: $stateParams.superheroId

)};

 

  • Scope 的目的是引用 model,而不是成为 model。
  • Model是我们的JavaScript对象

三、验证

1. 在form(表单)标签中使用“novalidate” 属性来使用 AngularJS验证同时关闭HTML5验证。

XHTML

 

1

<form name="reviewForm" ng-controller="ReviewController as reviewCtrl" ng-submit="reviewCtrl.addReview(product)" novalidate>

2. 我们可以使用angular classes(css类)来改变可见性和验证控件的状态。

CSS

 

1

2

3

4

5

6

.ng-valid.ng-dirty{

    border-color: #FA787E;

}

.ng-invalid.ng-dirty{

    border-color: #FA787E;

}

 

四、内存 – 任务管理

  • AngularJS在销毁一个scope和把一个scope从它的父级移除之前会广播一个$destroy事件。监听这个事件对清理任务和资源是至关重要的,否则可能会继续消耗内存或CPU。总是注册‘destroy‘事件来删除任何易于内存泄漏的代码。

作为一个例子,下面的控制器在1秒的间隔不断更新一个mode(模型)l值,这些更新将永远持续,即使在controller对应view消失了或者scope从父级上移除。如果用户来回导航到一个view来加载这个controller,每次导航将添加另一个永远运行的计时器。

JavaScript

 

1

2

3

4

5

6

7

8

9

module.controller("MyController", function($scope, $timeout) {

    var onMyTimeout = function() {

        $scope.value += 1;

        $timeout(onMyTimeout, 100);

    };

    $timeout(onMyTimeout, 100);

    $scope.value = 0;

 

});

 

监听$destroy事件是停止计时器的一个机会。一种方法是取消由$timeout返回的promise(承诺)。

JavaScript

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

module.controller("TestController", function($scope, $timeout) {var onTimeout = function() {

    $scope.value += 1;

    timer = $timeout(onTimeout, 100);

};

 

    var timer = $timeout(onTimeout, 100);

    $scope.value = 0;

    $scope.$on("$destroy", function() {

 

        if (timer) {

            $timeout.cancel(timer);

        }

    });

});

五、事件 顶级作用域

1. 我们应该只在当前屏幕的单页面应用程序的控制器中使用scope 事件进行通信。如果我们只需要共享数据,那么我们应该考虑使用Services(服务)。

2. 当触发事件时,除非我们需要把事件通知到我们整个应用程序里的所有单个scope,否则我们不需要在$rootScope触发事件。如果我们只需要为子scope触发,则在当前的scope上$broadcast事件。父scope使用$emit获取当前scope事件。这也将缩短事件传播,而不是经过整个自上而下的流动。

3. Services 是在$rootScope监听事件获取通知的不二选择。这是因为services在我们的应用程序中只初始化一次,并没有获取它们自己的scope ,这将是很好。

4. 通常我们不应该在$rootScope注册事件监听(除了service)。这是导致AngularJS应用程序产生bug的一个普遍原因。这是因为当我们在一个controller 的$scope上添加一个事件监听,而controller 被销毁(我们导航离开页面,或关闭一个部分),监听同样被销毁。当我们将它添加到$rootScope,同时导航离开一个controller,监听会保留并保持活动和触发。所以我们必须从$rootScope手动取消监听,或者为了安全干脆就不在$rootScope上添加监听。但是如果我们必须为$rootScope添加一个事件,不要忘记在controller的scope中将其清除。

 

1

2

3

4

5

6

&lt;strong&gt; &lt;/strong&gt;

 

var myEventHandler = $rootScope.$on(&#039;MyEvent&#039;, ‘My Data’);

      $scope.$on(&#039;$destroy&#039;, function() {

      myEventHandler();

});

5. 如果我们知道只有一个监听器,而且你已经遇到了。我们可以通过在传递给事件监听函数的事件对象上调用event.stopPropagation()来停止进一步的事件传播。

 

六、构建业务逻辑

1. Controllers 不应该引用DOM,而只是包含行为,使用Directives (指令)做DOM操作。

2. Services应该对view逻辑独立.

3. 不要与HTML打架,而是通过Directives扩展。

4. 最好是使用模块化的文件夹结构,这样我们可以创建可重用的/可分发的组件。

 

七、通用规则

1. 对images使用ng-src 替代src。

2. 使用promise 来处理回调。AngularJS已经为它暴露了“$q”服务。许多AngularJS services返回promises:$http, $interval, $timeout

3. 不要压缩angular.min.js 因为AngularJS团队已经通过预定义设置压缩了angular文件,如果我们再压缩可能会产生破坏。所以直接使用。

4. 如果template (模板)缓存是必需的,使用$templateCache缓存html template。

5. 总是把第三方API的回调包裹到$apply,用来通知AngularJS关于环境的变化。

6. 如果我们不想让用户在AngularJS加载之前显示HTML ,使用ng-cloak directive。

XHTML

 

1

2

3

4

5

<div class="session ng-cloak">..............content............</div>

.ng-cloak {

/* this will change to block when scope and angular is ready */

display:none;

}

7. 为了阻止任何冲突,不要在我们自己的directives里使用“ng”前缀。创建你的自定义的。最好使用<mycomponent> ,因为 <my:component>在IE有时出错。

JavaScript

 

1

2

<my-component>

<my:component>

8. 在应用程序中为全局相关的事件使用$broadcast() , $emit() 和 $on()(比如用户身份验证或应用程序关闭)。如果我们需要特定于模块,服务或小部件的事件,我们应该选择Services,Directive Controllers等。

9. 不要使用自动关闭标签,因为有些浏览器不喜欢他们。使用“<product-title></product-title >”而不是“<product-title/>”。

共有 人打赏支持
粉丝 0
博文 5
码字总数 28296
×
千里一线牵
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: