时隔一个星期,接着上一篇的angularjs应用骨架继续聊聊angularjs其余的其余的内容。javascript
在应用控制器中有三种职责:html
一、为应用中模型设置初始状态java
二、经过$scope对象把数据模型和函数暴露给视图(UI模版)angularjs
三、监视模型其他部分的变化,并采起相应的动做数组
我这主要想说的是第三项的功能:为了让控制器保持小巧可控制的状态,咱们的建议是,为视图中的每一块功能区域建立一个控制器。就是说若是你有一个菜单,就建立一个MenuController,若是你有一个面包屑导航就建立一个NavController,你可能已经明白,可是须要明确的是,控制器是绑定在DOM上的,这些片断就是它们须要管理的内容。有两种方式能够把控制器关联到DOM节点上,第一种是经过ng-controller属性来声明,另外一种是经过路由把它绑定到一个动态加载的DOM模版片断上,这个模版叫作视图。app
若是你的UI带有一些很是复杂的区域,你能够建立嵌套的控制器,它们能够经过继承树结构来共享数据模型和函数,这样你就能够保持代码的简单和可维护性。嵌套控制器很是的简单,只须要把控制器设置到DOM元素内部嵌套的元素上便可,示例以下:ide
1 <div ng-controller="ParentController"> 2 <div ng-controller="ChildController"></div> 3 </div>
虽然咱们把这中方式叫作控制器嵌套,但真实的嵌套发生在$scope对象上。经过内部的原型继承机制,父控制器对象上的$scope会被传递给内部嵌套控制器的$scope.具体到上面的例子就是,ChildController上的$socpe能够访问到ParentController上的$scope对象上的全部属性(和函数)。函数
利用向控制器传递$scope对象的机制,能够把模型数据暴露给视图。在你的应用中可能还有其余的数据,可是只有经过$scope触及这些数据,angular才会把它当成数据模型的一部分。你能够把$scope当成一个上下文环境,它让数据模型上的变化变得能够观察。性能
对于显式的建立$scope的属性咱们已经看见过许多例子。例如:$scope.count=5。还能够经过模版间接的建立数据模型。你能够经过一下几种方式来实现这一点。spa
一、经过表达式。既然表达式是在控制器中的$scope环境中执行的,而这个$scope与它们管理的元素有关,那么在表达式中设置属性和属性控制器中的$scope属性值是同样的 。也就是说,能够这样作:
1 <button ng-click="count=3"> Set count to 3</button> 2 <div ng-controller="CountController"> 3 <button ng-click="SetCount()">Set count to 3</button> 4 </div> 5 <script type="text/javascript"> 6 function CountController($scope){ 7 $scope.SetCount= function () { 8 $cope.count=3; 9 }; 10 } 11 </script>
二、在表单输入项上使用ng-modal。与表达式相似,ng-model上指定模型参数一样工做在外层控制器内。惟一不一样点在于,这样会在表单项和指定的模型之间创建双向绑定关系。
在$scope内置函数中,用的最多的就是$watch函数了,当你的数据模型中某一部分发生变化时,$watch函数能够向你发出通知。你能够监控单个对象的属性,也能够监控须要通过计算的结果(函数),实际上只要是可以看成属性访问到,或者能够看成一个javascript函数被计算出来,就能够被$watch函数监控。它的函数签名是$watch(watchFn,watchAction,deetWatch).
其中参数说明:
watchFn:该参数是一个带有angular表达式或者函数的字符串,它会返回被监控in个的数据模型的当前值。这个表达式会被执行不少次,因此要保证它不会长生其余反作用。也就是说,要保证它被调用不少次而不后悔改变状态。
watchAction:这是一个函数或者表达式,当watchFn发生变化时会被调用。若是是函数形式,它会接收到watchFn的新旧两个值,以及做用域对象的引用。其函数签名为function(newValue,oldValue,scope)。
deepWatch:若是设置为true,会告诉angualr去检查被监控对象的每一个值是否发生变化。那么运算负担就会比较重。
示例:当用户添加到购物车中的价值超过美圆的时候就会给美圆的折扣:
1 <!DOCTYPE html> 2 <html ng-app="MyApp"> 3 <head> 4 <meta charset="UTF-8"> 5 <title></title> 6 <script src="static/js/angular.js" type="text/javascript"></script> 7 <script src="static/app/controller/shopingcartcontroller.js" type="text/javascript"></script> 8 </head> 9 <body ng-controller="myController"> 10 <div> 11 <h1>Your Shoping Cart</h1> 12 <div ng-repeat="item in items"> 13 <span>{{item.title}}</span> 14 <input type="text" ng-model="item.quantity"/> 15 <span>{{item.quantity | currency}}</span> 16 <span>{{item.quantity * item.price | currency}}</span> 17 <button ng-click="remove($index)">remove</button> 18 </div> 19 20 <div>total:{{totalCart()| currency}}</div> 21 <div>Discount:{{bill.discount | currency}}</div> 22 <div>Subtotal:{{ subtotal() | currency}}</div> 23 </div> 24 </body> 25 </html>
1 /** 2 * Created by Administrator on 2015/6/12. 3 */ 4 var app=angular.module('MyApp',[]); 5 app.controller('myController', function ($scope) { 6 $scope.bill={}; 7 $scope.items=[ 8 {title:"羽毛球",quantity:8,price:3.99}, 9 {title:"篮球",quantity:17,price:1.99}, 10 {title:"足球",quantity:5,price:3.99} 11 ]; 12 13 $scope.remove=function(index){ 14 $scope.items.splice(index,1); 15 } 16 17 18 $scope.totalCart= function () { 19 var total=0; 20 for(var i= 0,len=$scope.items.length;i<len;i++){ 21 total=total + $scope.items[i].price * $scope.items[i].quantity; 22 } 23 return total; 24 }; 25 26 $scope.subtotal= function () { 27 return $scope.totalCart() - $scope.discount; 28 }; 29 30 31 function calulateDdiscount(newvalue,oldvalue,scope){ 32 $scope.bill.discount=newvalue > 100 ? 10 :0; 33 }; 34 35 $scope.$watch($scope.totalCart,calulateDdiscount); 36 });
效果图:
虽然前面的例子可以正确的执行,可是却存在潜在的性能问题。若是你在totalCart()上打一个断点,你会看到,渲染页面时该函数被调用了6次。那么问题来了,为何会是6次呢?让咱们来分析一下:其中的三次咱们能容易的追踪到:
一、模版{{totalCart() | currency}}
二、subtotal()函数
三、$watch()函数
而后angualr又把上诉过程重复一边,最终就是6次。固然有方案解决问题:一种是监控数组的变化,而后从新计算$scope属性中的总价、折扣和小计值。
为了方便,咱们把模版修改为这样:
1 <div>total:{{bill.total| currency}}</div> 2 <div>Discount:{{bill.discount | currency}}</div> 3 <div>Subtotal:{{ bill.subtotal | currency}}</div>
咱们会监控items数组,当数组发生任何变化时,调用一个函数来计算总价,以下:
1 <!DOCTYPE html> 2 <html ng-app="MyApp"> 3 <head> 4 <meta charset="UTF-8"> 5 <title></title> 6 <script src="static/js/angular.js" type="text/javascript"></script> 7 <script src="static/app/controller/shopingcartcontroller.js" type="text/javascript"></script> 8 </head> 9 <body ng-controller="myController"> 10 <div> 11 <h1>Your Shoping Cart</h1> 12 <div ng-repeat="item in items"> 13 <span>{{item.title}}</span> 14 <input type="text" ng-model="item.quantity"/> 15 <span>{{item.quantity | currency}}</span> 16 <span>{{item.quantity * item.price | currency}}</span> 17 <button ng-click="remove($index)">remove</button> 18 </div> 19 20 <div>total:{{bill.totalCart| currency}}</div> 21 <div>Discount:{{bill.discount | currency}}</div> 22 <div>Subtotal:{{ bill.subtotal | currency}}</div> 23 </div> 24 </body> 25 </html>
1 /** 2 * Created by Administrator on 2015/6/12. 3 */ 4 var app = angular.module('MyApp', []); 5 app.controller('myController', function ($scope) { 6 $scope.bill = {}; 7 $scope.items = [ 8 {title: "羽毛球", quantity: 8, price: 3.99}, 9 {title: "篮球", quantity: 17, price: 1.99}, 10 {title: "足球", quantity: 5, price: 3.99} 11 ]; 12 13 $scope.remove = function (index) { 14 $scope.items.splice(index, 1); 15 } 16 17 var calulateTotal = function () { 18 var total = 0; 19 for (var i = 0, len = $scope.items.length; i < len; i++) { 20 total = total + $scope.items[i].price * $scope.items[i].quantity; 21 } 22 $scope.bill.totalCart = total; 23 $scope.bill.discount = total > 100 ? 10 : 0; 24 $scope.bill.subtotal=total - $scope.bill.discount; 25 }; 26 27 $scope.$watch("items", calulateTotal,true); 28 });
效果图就不上了,和上面是同样的。可是你在calulateTotal()函数上打个断点,你会发现页面渲染时只调用了一次。这样在性能上就有了很大的提高。请注意,上面的代码在调用$watch函数时把items写成了一个字符串,那是由于$watch函数能够接受一个函数也能够接受一个字符串,若是把一个字符串传给函数,将会在被调用的$scope做用域中看成表达式来执行。可是,监控items数组,那么angular就要拷贝一份items数组用来比较操做。对于大型的items数组来讲,若是angular每次显示页面只须要从新计算bill的值那么性能会好不少。
模版没有改变,控制器发生变化:
1 /** 2 * Created by Administrator on 2015/6/12. 3 */ 4 var app = angular.module('MyApp', []); 5 app.controller('myController', function ($scope) { 6 $scope.bill = {}; 7 $scope.items = [ 8 {title: "羽毛球", quantity: 8, price: 3.99}, 9 {title: "篮球", quantity: 17, price: 1.99}, 10 {title: "足球", quantity: 5, price: 3.99} 11 ]; 12 13 $scope.remove = function (index) { 14 $scope.items.splice(index, 1); 15 } 16 17 //var calulateTotal = function () { 18 // var total = 0; 19 // for (var i = 0, len = $scope.items.length; i < len; i++) { 20 // total = total + $scope.items[i].price * $scope.items[i].quantity; 21 // } 22 // $scope.bill.totalCart = total; 23 // $scope.bill.discount = total > 100 ? 10 : 0; 24 // $scope.bill.subtotal=total - $scope.bill.discount; 25 //}; 26 27 $scope.$watch(function () { 28 var total = 0; 29 for (var i = 0, len = $scope.items.length; i < len; i++) { 30 total = total + $scope.items[i].price * $scope.items[i].quantity; 31 } 32 $scope.bill.totalCart = total; 33 $scope.bill.discount = total > 100 ? 10 : 0; 34 $scope.bill.subtotal=total - $scope.bill.discount; 35 }); 36 });
若是想监控多个属性或对象,而且其中任何一个发生变化时就去执行一个函数。那该怎么作呢?其实在上面的例子中已经体现出来了。就是有三个商品,无论哪一个商品的数量发生改变都会执行calulateTotal()函数,而且我在$watch函数的第三个参数设置成true。
基本上解决上诉问题有两个选择:
一、监控这些属性链接起来以后的值
二、把它们放到一个数组和对象中,而后个deepWatch传递一个true值
如:
$scope.$watch('things.a + things.b',callMe());
或者
$scope.$watch('things',callMe(),true);