angularjs應用骨架(2)

  時隔一個星期,接着上一篇的angularjs應用骨架繼續聊聊angularjs其餘的其餘的內容。javascript

  區分UI和控制器的職責

  在應用控制器中有三種職責: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>
View Code

  雖然咱們把這中方式叫作控制器嵌套,但真實的嵌套發生在$scope對象上。經過內部的原型繼承機制,父控制器對象上的$scope會被傳遞給內部嵌套控制器的$scope.具體到上面的例子就是,ChildController上的$socpe能夠訪問到ParentController上的$scope對象上的全部屬性(和函數)。函數

  利用$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>
View Code

二、在表單輸入項上使用ng-modal。與表達式相似,ng-model上指定模型參數一樣工做在外層控制器內。惟一不一樣點在於,這樣會在表單項和指定的模型之間創建雙向綁定關係。

  使用$watch監控數據模型的變化

  在$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>
View Code
 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 });
View Code

效果圖:

$watch()中的性能注意事項

  雖然前面的例子可以正確的執行,可是卻存在潛在的性能問題。若是你在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>
View Code

  咱們會監控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>
View Code
 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 });
View Code

 效果圖就不上了,和上面是同樣的。可是你在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 });
View Code

 

  

   監控多個東西

  若是想監控多個屬性或對象,而且其中任何一個發生變化時就去執行一個函數。那該怎麼作呢?其實在上面的例子中已經體現出來了。就是有三個商品,無論哪一個商品的數量發生改變都會執行calulateTotal()函數,而且我在$watch函數的第三個參數設置成true。

  基本上解決上訴問題有兩個選擇:

  一、監控這些屬性鏈接起來以後的值

  二、把它們放到一個數組和對象中,而後個deepWatch傳遞一個true值

  如:

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

  或者

  $scope.$watch('things',callMe(),true);

相關文章
相關標籤/搜索