好,繼續上一章節咱們繼續聊聊angularjs骨架。開發任何一款優秀的應用都會面臨一項很是困難的工做,那就是找到一種合適的方式方法把代碼組織在合適的功能範圍內。咱們已經看過控制器的處理方式,它會提供一塊空間,把正確的數據模型和函數暴露給視圖模版。可是用來支持咱們應用的代碼該怎麼辦呢?有一塊最明顯的能夠放在這些代碼的地方,那就是控制器的函數。javascript
這種作法對於小型的應用和例子來講能夠工做的很好,咱們已經看到過不少這樣的例子,可是在真實的應用中,這種作法很快就會使得代碼沒法維護。控制器將會變成一個垃圾場,咱們作的全部東西都會倒在裏面。它們會變得很是難以理解,而且很難修改。html
咱們來看模塊,它們提供了一種方法,能夠用來組織應用中一塊功能區域的依賴關係;同時還提供了一種機制,能夠自動解析依賴關係(又叫作依賴注入)。通常來講,咱們把這些東西叫作依賴服務。由於它們負責爲應用提供特殊服務。java
例如,咱們要在購物站點中的一個控制器中須要從服務器上獲取一個商品列表,那麼咱們就須要這些對象——不妨把它叫作Items——用來處理服務器端獲取商品列表的工做,進而,Items對象就須要以某種方式與服務器上的數據庫進行交互,能夠經過XHR或者WebSocket。angularjs
若是沒有模塊的狀況下,咱們的控制器看起來可能就是這樣:數據庫
1 function ItemsViewController(){ 2 //向服務器發起請求 3 4 //解析響應並放入Items對象 5 6 //把Items數組放到$scope上,這樣視圖才能顯示它 7 }
雖然這樣能夠運行,可是卻存在大量潛在的問題:express
一、若是其餘控制器也要從服務器上獲取Items,那麼只能把這段代碼再寫一遍,這會給維護工做帶來很大的負擔。數組
二、加上一些其餘因素,例如服務端認證、解析數據的複雜性,會使得控制器對象很難劃分出一個合理的功能邊界,而且代碼閱讀起來很是困難。瀏覽器
三、使得單元測試變得複雜。服務器
利用模塊和模塊內置的依賴注入的功能,就能夠把控制器變得簡單,示例以下:app
1 app.controller('myController', function ($scope,Items) { 2 $scope.items =Items.query(); 3 …… 4 }
你可能會問,「不錯,看起來還不錯,可是Items是什麼東西,從哪裏來?」以上代碼,假如咱們已經把Items變成了一個服務。服務都是單例的對象,它們用來執行必要的任務支撐應用的功能。
Angular內置了不少服務,例如$location服務,用來和瀏覽器的地址欄進行交互;$route服務,用來根據URL地址的變化切換視圖;還有$http服務,用來和服務器進行交互。你也可已建立本身的服務,用它們來實現你的應用所特有的功能,若是須要,服務能夠在任何控制器中共享,當你須要多個控制器之間進行交互和共享狀態時這些服務就是一種很好的機制。Angular的內置服務一$開頭,你固然能夠爲你的服務隨意起名字,可是最好不要以$開頭,以避免引發命名衝突。你可使用模型對象的API來定義服務。一下三種函數能夠用來建立通常的服務,它們的複雜度和功能度不一樣。
函數1:provider(name,Object or constructor()),定義:一個可配置的服務,建立的邏輯比較複雜。
函數2:factory(name,$getFunction() ),定義:一個不可配置的服務,建立邏輯比較複雜。能夠把它當作provider(name,{$get:$getFunction()})。
函數3:servive(name,conttuctor()),定義:一個不可配置的服務,建立邏輯比較簡單。
咱們先來看一個使用factory建立服務的例子。咱們能夠像下面這樣來編寫服務:
1 var app = angular.module('MyApp', []); 2 app.factory('Items', function () { 3 var items={}; 4 items.query= function () { 5 return [ 6 {title: "羽毛球", quantity: 8, price: 3.99}, 7 {title: "籃球", quantity: 17, price: 1.99}, 8 {title: "足球", quantity: 5, price: 3.99} 9 ]; 10 }; 11 return items; 12 });
爲了能讓這一機制可以和模版配合起來,咱們把模版名稱告訴ng-app指令。
完整代碼以下:
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.factory('Items', function () { 6 var items={}; 7 items.query= function () { 8 return [ 9 {title: "羽毛球", quantity: 8, price: 3.99}, 10 {title: "籃球", quantity: 17, price: 1.99}, 11 {title: "足球", quantity: 5, price: 3.99} 12 ]; 13 }; 14 return items; 15 }); 16 app.controller('myController', function ($scope,Items) { 17 $scope.bill = {}; 18 $scope.items =Items.query(); 19 20 $scope.remove = function (index) { 21 $scope.items.splice(index, 1); 22 } 23 24 //var calulateTotal = function () { 25 // var total = 0; 26 // for (var i = 0, len = $scope.items.length; i < len; i++) { 27 // total = total + $scope.items[i].price * $scope.items[i].quantity; 28 // } 29 // $scope.bill.totalCart = total; 30 // $scope.bill.discount = total > 100 ? 10 : 0; 31 // $scope.bill.subtotal=total - $scope.bill.discount; 32 //}; 33 34 $scope.$watch(function () { 35 var total = 0; 36 for (var i = 0, len = $scope.items.length; i < len; i++) { 37 total = total + $scope.items[i].price * $scope.items[i].quantity; 38 } 39 $scope.bill.totalCart = total; 40 $scope.bill.discount = total > 100 ? 10 : 0; 41 $scope.bill.subtotal=total - $scope.bill.discount; 42 }); 43 44 45 46 47 function ItemsViewController(){ 48 //向服務器發起請求 49 50 //解析響應並放入Items對象 51 52 //把Items數組放到$scope上,這樣視圖才能顯示它 53 } 54 });
固然你若是你須要使用第三個方包中的所提供的服務或指令,它們通常會有本身的模塊,因此須要在應用中定義依賴關係才能應用它們。假如你引入了模塊A和B,那麼應用中 的模塊聲明看起來可能會像下面這樣:
var appMod=agular.module('myapp',['A','B']);
你可使用過濾器來聲明如何變換數據顯示的格式,而後在展現給用戶,你只要在模版中使用一個插值變量便可。使用過濾器的語法是:
{{expression | filterName : parameter1 : parameter2}}
過濾器中的多個參數使用冒號隔開。
Angular中有不少過濾器,如currency
{{12.9 | currency}}
以上代碼獲得結果就是$12.90
在綁定過程當中使用管道符號把過濾器鏈接起來。例如:
{{12.9 | currency | number}}
獲得的結果是$13
固然,你也沒必要受限與內置過濾器,你也能夠自定義過濾器:
1 <p> {{ 'my name is amber.xu' | titleCase }}</p>
1 app.filter('titleCase', function () { 2 var titleCaseFilter= function (input) { 3 var words=input.split(' '); 4 for(var i=0;i<words.length;i++){ 5 words[i]=words[i].charAt(0).toUpperCase() + words[i].slice(1); 6 } 7 return words.join(' '); 8 }; 9 return titleCaseFilter; 10 });
效果以下:
My Name Is Amber.xu
雖然從技術上來講AJAX應用確實是單頁應用,可是在不少時候,出於各類緣由,咱們須要爲用戶展現或隱藏一些子頁面視圖。
咱們能夠利用angular的$route服務來管理這種場景,能夠利用路由服務來定義這樣一種東西:對於瀏覽器的一種url,angular將會加載一中視圖模版,並實例化一個控制器爲模版提供內容。在應用中你可使用$routeProvider服務上的函數來建立路由,把須要建立的路由當成一個配置塊來傳給函數便可。建立過程相似一下的僞代碼:
1 app.config(function ($routeProvider) { 2 $routeProvider.when('url',{controller:aController,templateUrl:'/path/to/template'}). 3 when(...other mapping for your app...). 4 ... 5 otherwise(...waht to do if nothing else matches...) 6 });
以上代碼是說,當瀏覽器中的URL發生變成指定的取值時,angular將會加載/path/to/template路徑下的模版,而後把模版中的跟元素關聯到aController上(由於咱們在模版中寫了ng-controller=aController)最後一行的otherwise至關於else或者default。
既然,理論上的東西都有了。咱們來實戰一下吧。我寫這個demo的時候也遇到了一些bug,不妨爆出來給你們看看:
錯誤1:
這個錯出現的緣由是說$routeProvider根本沒有被找到,爲何會沒有被找到了,你可能會說,我已經導入了angular.js文件啦?其實否則,路由已經被單獨分離出來了,因此要導入angular-route.js文件的引用,以前有講過,若是使用了第三方包中提供的指令就要在模塊中引用它:var app=angular.module('AMail',['ngRoute']);若是值導入了文件,而在代碼中沒有對ngRoute進行引用會出現此錯誤:
如今須要一個首頁index.html
1 <!DOCTYPE html> 2 <html ng-app="AMail"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>AMail</title> 6 <script src="../static/js/angular.js" type="text/javascript"></script> 7 <script src="../static/js/angular-route.min.js"></script> 8 <script src="../static/app/controller/controller.js" type="text/javascript"></script> 9 </head> 10 <body> 11 <h1>A-Mail</h1> 12 <div ng-view> 13 14 </div> 15 </body> 16 </html>
index.html上有一個以前沒有見過的指令ng-view。ng-view會把模版中的內容替換到這裏(至關於一個佔位符,說的比較口語化)。
有兩個模版
一、list.html顯示全部的郵件列表。
二、detail.html單個郵件的詳細頁。
1 <table> 2 <tr> 3 <td><strong>Sender</strong></td> 4 <td><strong>Subject</strong></td> 5 <td><stroong>Date</stroong></td> 6 </tr> 7 <tr ng-repeat="message in messages"> 8 <td>{{message.sender}}</td> 9 <td><a href="#/view/{{message.id}}">{{message.subject}}</a></td> 10 <td>{{message.date}}</td> 11 </tr> 12 </table>
1 <div><strong>Subject:</strong>{{message.subject}}</div> 2 <div><strong>Sender:</strong>{{message.sender}}</div> 3 <div><strong>Date:</strong>{{message.date}}</div> 4 <div><strong>To:</strong> 5 <span ng-repeat="re in message.recipients"> {{re}}</span> 6 </div> 7 <div>{{message.message}}</div> 8 <a href="#/">Back to Message</a>
1 !(function () { 2 var app = angular.module('AMail', ['ngRoute']); 3 4 function emailRouteCoinfig($routeProvider) { 5 $routeProvider.when('/', {controller: ListController, templateUrl: 'list.html'}). 6 when('/view/:id', {controller: DetailController, templateUrl: 'detail.html'}). 7 otherwise({redirectTo: '/'}); 8 }; 9 //配置咱們的路由,以便AMail可以找到他 10 app.config(emailRouteCoinfig); 11 12 //一些虛擬的喲意見 13 var messages = [ 14 { 15 id: 0, 16 sender: 'jean@yesno.com.cn', 17 subject: '今天不用加班啦,明天週末', 18 date: '2015年7月14日 21:39:59', 19 message: '今天不用加班啦,明天週末!明天是週末,能夠好好休息兩天了。', 20 recipients: ['woshixuleijava@163.com'] 21 }, 22 { 23 id: 1, 24 sender: 'jean2@yesno.com.cn', 25 subject: '明天不用加班啦,明天週末', 26 date: '2015年7月15日 21:39:59', 27 message: '明天不用加班啦,明天週末!明天是週末,能夠好好休息兩天了。', 28 recipients: ['woshixuleijava2@163.com'] 29 }, 30 { 31 id: 2, 32 sender: 'jean3@yesno.com.cn', 33 subject: '後天不用加班啦,明天週末', 34 date: '2015年7月16日 21:39:59', 35 message: '後天不用加班啦,明天週末!明天是週末,能夠好好休息兩天了。', 36 recipients: ['woshixuleijava3@163.com'] 37 } 38 ]; 39 40 function ListController($scope) { 41 $scope.messages = messages; 42 }; 43 function DetailController($scope, $routeParams) { 44 $scope.message = messages[$routeParams.id]; 45 }; 46 47 48 }());
效果圖:
咱們已經爲應用搭好了基本框架,這款應用帶有不少視圖,能夠經過修改url的方式來切換視圖。這就意味着對用戶來講,前進後退按鈕能真正運行起來了。而這裏實際上只有一個真實的HTML頁面。