6、表达式

   在模板中使用表达式是为了以充分的灵活性在模板、业务逻辑和数据之间建立联系,同时又能避免让业务逻辑渗透到模板中。

 <div ng-controller="SomeController">
     <div>{{recompute() / 10}}</div>
     <ul ng-repeat="thing in things">
         <li ng-class="{highlight: $index % 4 >= threshold($index)}">
             {{otherFunction($index)}}
         </li>
     </ul>
 </div>

  当然,对于第一个表达式,recompute() / 10,应该避免这种把业务逻辑放到模板中的方式,应该清晰地区分视图和控制器之间的职责,这样更便于测试。

  虽然 Angular 里面的表达式比 Javascript 更严格,但是它们对 undefined 和 null 的容错性更好。如果遇到错误,模板只是简单地什么都不显示,而不会抛出一个 NullPointerException 错误,这样你就可以安全地使用未经初始化的模型值,而一旦它们被赋值以后就会立即显示出来。

7、区分 UI 和控制器的职责

  在应用中控制器有三种职责:

  • 为应用中的模型设置初始状态
  • 通过 $scope 对象把数据模型和函数暴露给视图(UI模板)
  • 监视模型其余部分的变化,并采取相应的动作

    建议为视图中的每一块功能区域创建一个控制器,这样可以让控制器保持小巧和可管理的状态。

  更为复杂的时候,你可以创建嵌套的控制器,通过内部的原型继承机制,父控制器对象上的 $scope 会被传递给内部嵌套控制器的 $scope。

 <div ng-controller="ParentController">
     <div ng-controller="ChildController">...</div>
 </div>

  上面的例子中,ChildController 的 $scope 对象可以访问 ParentController 的 $scope 对象上的所有属性(和函数)。

8、使用 $watch 监控数据模型的变化

   在 scope 内置的所有函数中,用到最多的可能就是 $watch 函数了,当你的数据模型中的某一部分发生变化时,$watch 可以向你发出通知,监控单个对象的属性,也可以监控需要经过计算的结果,只要能被当作属性访问到,或者可以当作一个 JavaScript 函数被计算出来,就可以被 $watch 函数监控。函数签名为:

  $watch(watchFn, watchAction, deepWatch)

  watchFn —— 该参数是一个带有 Angular 表达式或者函数的字符串,返回被监控的数据模型的当前值。它会被执行多次,所以要保证不会产生副作用;

  watchAction —— 通常以函数的形式,接收到 watchFn 的新旧两个值,以及作用域对象的引用,函数签名为 function(newValue, oldValue, scope)

  deepWatch —— 如果设置为 true,则会去检查被监控对象的每个属性是否发生了变化,一般监控一个数组或者多个对象时要设置为 true,但由于要遍历数组,所以运算负担会比较重。

   $watch 函数会返回一个属性,当你不再需要接受变更通知时,可以用这个返回的函数注销监控器。如果需要监控一个属性,然后注销监控,可以如下:

 ...
 var dereg = $scope.$watch('someModel.someProperty', callbackOnChange());
 ...
 dereg();

  一个购物车的例子,当用户添加到购物车中的商品价格超过 100 美元的时候,会有 10 美元的折扣。使用下面的模板:

 <html ng-app="shoppingCart">
 <body>
 <div ng-controller="CartController">
     <div ng-repeat="item in items">
         <span>{{item.title}}</span>
         <input ng-model="item.quantity">
         <span>{{item.price | currency}}</span>
         <span>{{item.price * item.quantity | currency}}</span>
     </div>
     <div>Total: {{totalCart() | currency}}</div>
     <div>Discount: {{bill.discount | currency}}</div>
     <div>Subtotal: {{subtotal() | currency}}</div>
 </div>
 <script src="src/angular.js"></script>
 <script src="shoppingCart.js"></script>
 </body>
 </html>

  控制器如下:

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

 shoppingCart.controller('CartController', function ($scope) {
     $scope.bill = {};

     $scope.items = [
         {title: 'Paint pots', quantity: 8, price: 3.95},
         {title: 'Polka dots', quantity: 17, price: 12.95},
         {title: 'Pebbles', quantity: 5, price: 6.95}
     ];

     $scope.totalCart = function () {
         var total = 0;
         for (var i = 0, len = $scope.items.length; i < len; i++) {
             total = total + $scope.items[i].price * $scope.items[i].quantity;
         }
         return total;
     };

     function calculateDiscount(newValue, oldValue, scoope) {
         $scope.bill.discount = newValue > 100 ? 10 : 0;
     }

     $scope.subtotal = function () {
         return $scope.totalCart() - $scope.bill.discount;
     };

     $scope.$watch($scope.totalCart, calculateDiscount);
 });

  用户看到的效果如图:

  

9、watch() 中的性能注意事项

  断点调试 totalCart() 的代码,你会发现渲染这个页面时,该函数被调用了 6 次。其中 3 次发生在每次调用它的时候:

  • 模板 {{totalCart() | currency}}
  • subtotal() 函数
  • $watch() 函数

   然后 Angular 又把整个过程重复了一遍,这样的目的是:检测模型中的变更已经被完整地进行了传播,并且模型已经被设置好。Angular 的做法是,把所有被监控的属性都拷贝一份,然后把它们和当前的值进行比较,看看是否发生了变化。实际上,Angular 可能运行上面过程不止两遍,如果重复 10 遍发现属性还在变化,Angular 会报错并退出,这时候你需要解决循环依赖的问题了。

   PS: 书里面说的是,由于 Angular 需要用 JavaScript 实现数据绑定,官方开发团队与 TC39 团队一起开发了一个叫做 Object.observe() 的底层本地化实现,这样可以让你的数据绑定操作就像本地化代码一样快速。

   我们可以改成监控 items 数组的变化,然后重新计算 $scope 属性中的总价、折扣和小计值来减少函数调用次数。

   模板修改如下:

 <div>Total: {{totalCart() | currency}}</div>
 <div>Discount: {{bill.discount | currency}}</div>
 <div>Subtotal: {{subtotal() | currency}}</div>

  控制器修改如下:

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

 shoppingCart.controller('CartController', function ($scope) {
     $scope.bill = {};

     $scope.items = [
         {title: 'Paint pots', quantity: 8, price: 3.95},
         {title: 'Polka dots', quantity: 17, price: 12.95},
         {title: 'Pebbles', quantity: 5, price: 6.95}
     ];

     function calculateDiscount(newValue, oldValue, scoope) {
         var total = 0;
         for (var i = 0, len = $scope.items.length; i < len; i++) {
             total = total + $scope.items[i].price * $scope.items[i].quantity;
         }
         $scope.bill.totalCart = total;
         $scope.bill.discount = newValue > 100 ? 10 : 0;
         $scope.bill.subtotal = total - $scope.bill.discount;
     }

     $scope.$watch('items', calculateDiscount, true);
 });

  在调用 $watch 函数时把 items 写成了一个字符串,并且第三个参数设置为 true,可以监控整个 items 数组的变化,单需要制作一份数组的拷贝,用来进行比较操作。

  所以,如果每次在 Angular 显示页面时只需要重新计算 bill 属性,那么性能会好很多。可以像下面这样重新计算属性值:

 $scope.$watch(function (newValue, oldValue, scope) {
     var total = 0;
     for (var i = 0, len = $scope.items.length; i < len; i++) {
         total = total + $scope.items[i].price * $scope.items[i].quantity;
     }
     $scope.bill.totalCart = total;
     $scope.bill.discount = newValue > 100 ? 10 : 0;
     $scope.bill.subtotal = total - $scope.bill.discount;
 });

  根据性能分析更是说明了这点(左边是第二种方式):

  当然可以使用上面说过的方法,利用 $watch 返回的函数,移除不必要的 $watch,参见破狼大神的例子:Angular 移除不必要的 $watch

10、监控多个东西

  监控多个东西,有两种基本的选择:

  • 监控把这些属性连接起来之后的值
  • 把它们放到一个数组或者对象中,然后给 deepWatch 参数传递一个 true 值。

  第二种情况上面已经介绍,第一种情况比较简单,比如在你的作用域中存在一个 things 对象,它带有两个属性 a 和 b,当这两个属性发生变化时都需要执行 callMe() 函数,你可以同时监控着两个属性, 示例如下:

$scope.$watch('things.a + things.b', callMe(...));

  当然,a 和 b 也可以属于不同的对象,这个列表可以很长,不过如果需要监控的属性比较多, 不妨把这个列表放入一个函数中,返回连接的值。

特别感谢《用 AngularJS 开发下一代 Web 应用》

AngularJS 的常用特性(三)的更多相关文章

  1. AngularJS 的常用特性(五)

    13.使用路由和 $location 切换视图 对于一些单页面应用来说,有时候需要为用户展示或者隐藏一些子页面视图,可以利用 Angular 的 $route 服务来管理这种场景. 你可以利用路由服务 ...

  2. AngularJS 的常用特性(一)

    前言:AngularJS 是一款来自 Google 的前端 JS 框架,该框架已经被应用到了 Google 的多款产品中,这款框架最核心特性有:MVC.模块化.自动化双向数据绑定.语义化标签.依赖注入 ...

  3. AngularJS 的常用特性(四)

    11.使用 Module(模块) 组织依赖关系 Angular 里面的模板,提供了一种方法,可以用来组织应用中一块功能区域的依赖关系:同时还提供了一种机制,可以自动解析依赖关系(又叫依赖注入),一般来 ...

  4. AngularJS 的常用特性(二)

    3.列表.表格以及其他迭代型元素 ng-repeat可能是最有用的 Angular 指令了,它可以根据集合中的项目一次创建一组元素的多份拷贝. 比如一个学生名册系统需要从服务器上获取学生信息,目前先把 ...

  5. MVC常用特性

    MVC常用特性使用   简介 在以前的文章中,我和大家讨论如何用SingalR和数据库通知来完成一个消息监控应用. 在上一篇文章中,我介绍了如何在MVC中对MongoDB进行CRUD操作. 今天,我将 ...

  6. C#网络程序设计(1)网络编程常识与C#常用特性

        网络程序设计能够帮我们了解联网应用的底层通信原理!     (1)网络编程常识: 1)什么是网络编程 只有主要实现进程(线程)相互通信和基本的网络应用原理性(协议)功能的程序,才能算是真正的网 ...

  7. AngularJS 最常用的几种功能

    AngularJS 最常用的几种功能 2017-04-13 吐槽阿福 互联网吐槽大会 第一 迭代输出之ng-repeat标签ng-repeat让table ul ol等标签和js里的数组完美结合 1 ...

  8. Unity3D编辑器扩展(五)——常用特性(Attribute)以及Selection类

    前面写了四篇关于编辑器的: Unity3D编辑器扩展(一)——定义自己的菜单按钮 Unity3D编辑器扩展(二)——定义自己的窗口 Unity3D编辑器扩展(三)——使用GUI绘制窗口 Unity3D ...

  9. mootools常用特性和示例(基础篇1)

    网上关于mootools这个库的信息很少. 公司一些老的项目用到了mootools库,因为要维护,所以接触到了mootools. mootools(文档)官网:http://www.chinamoot ...

随机推荐

  1. Centos7下修复 视频播放器(先 安装VLC视频播放器)

    用最新的CentOS7发现没有视频播放器,于是在http://pkgs.org/上查找,发现了nux dextop仓库上有, 于是到他的官网上http://li.nux.ro/repos.html查了 ...

  2. vim 命令全

    1 简介 vim是文本编辑器.代码补完.编译及错误跳转等方便编程的功能特别丰富,在程序员中被广泛使用.下面讲述一些必要的基本命令,而掌握好这些命令,您就能够很容易将vim当作一个通用的万能编辑器来使用 ...

  3. (转)Struts2返回JSON数据的具体应用范例

    转载自 yshjava的个人博客主页 <Struts2返回JSON数据的具体应用范例> 早在我刚学Struts2之初的时候,就想写一篇文章来阐述Struts2如何返回JSON数据的原理和具 ...

  4. c语言博客作业06-文件

    1.本章总结  1.1思维导图  1.2本章学习体会 这周学了结构体和文件,结构体作为一种数据的归类方式,相比数组或变量更具有整体全面性,例如一个数组只可以放一些按照元素顺序存放的单元变量,并且我们用 ...

  5. ceph 移除 osd

    1:从crush中移除节点ceph osd crush remove osd.0 2:删除节点ceph osd rm osd.0 3:删除节点认证(不删除编号会占住)ceph auth del osd ...

  6. “全栈2019”Java第三章:安装开发工具IntelliJ IDEA

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  7. Jzoj 初中2249 蒸发学水(并查集)

    题目描述 众所周知,TerryHu 是一位大佬,他平时最喜欢做的事就是蒸发学水. 机房的位置一共有n 行m 列,一开始每个位置都有一滴学水,TerryHu 决定在每一个时刻选择 一滴学水进行蒸发,直到 ...

  8. Linux 下的 netfilter 认识与常规操作

    Linux 下的 netfilter 认识与常规操作 前言 博客写到今天,1年7个月.可是包含所有写作经历,这个时间线可以达到三年. 上次更新了一篇 "镇站之宝" ,也是本站阅读量 ...

  9. 高可用群集HA介绍与LVS+keepalived高可用群集

    一.Keepalived介绍 通常使用keepalived技术配合LVS对director和存储进行双机热备,防止单点故障,keepalived专为LVS和HA设计的一款健康检查工具,但演变为后来不仅 ...

  10. python基础知识梳理----6set 集合的应用

    集合内容简介: set 一: 集合简介 集合set集合是python的一个基本数据类型.一般不是很常用set.中的元素是不重复的.无序的.里里面的元素必须是可hash的tuple,bool),str, ...