11、使用 Module(模組) 組織依賴關係
Angular 裡面的模板,提供了一種方法,可以用來組織應用中一塊功能區域的依賴關係;同時還提供了一種機制,可以自動解析依賴關係(又叫依賴注入),一般來說,我們把這些叫做依賴服務,因為它們會負責為應用提供特殊的服務。
例如,如果購物站點中的一個控制器需要從伺服器中獲取一個商品列表 Items,來處理從伺服器獲取商品的工作。進而,Items 物件就需要以某種方式與伺服器上的資料庫進行互動,可以通過 XHR 或者 WebSocket。
如果,在沒有模組的情況下,那麼程式碼看起來就像下面這樣:
function ItemsViewController($scope) { // 向伺服器發起請求 ... // 解析響應並放入 Item 物件 ... // 把 Items 陣列設定到 $scope 上,這樣檢視才能夠顯示它 ... }
雖然這麼做可以執行,但是存在一些潛在的問題:
- 如果其他控制器也需要從伺服器獲取 Items,那麼我們只能把這段程式碼重寫一遍,程式碼維護工作增大難度;
- 加上其他的一些因素,例如伺服器認證、解析資料的複雜性,會使控制器物件很難劃分出一個合理的功能邊界,並且程式碼閱讀起來更加困難;
- 增加單元測試的難度,要測試這段程式碼,必須啟動一個真正的伺服器。
利用模組和模組內建的依賴注入功能,我們可以把控制器寫得更加簡單,示例如下:
function ShoppingController($scope, Items) { $scope.items = Items.query(); }
這樣會很簡單而且方便,只需要把 Items 物件定義成了一個服務。
服務都是單例的物件,Angular 內建了很多服務,例如 $location 服務,用來和瀏覽器的位址列進行互動;$route 服務,用來根據 URL 地址的變化切換檢視;還有 $http 服務,用來和伺服器進行互動。
你也可以自定義自己的服務,但最好不要以 $ 開頭,因為 Angular 內建的服務是以 $ 符號開頭的,以免引起衝突。
你可以使用模型物件的 API 來定義服務,如下表格:
——————————————————————————————————————————————————————————————————————
函式 | 定義
——————————————————————————————————————————————————————————————————————
provider(name, Object OR constructor()) | 一個可配置的服務,如果你傳遞了一個 Object 作為引數,那麼該物件必須帶有一個名為 $get 的函式,
| 該函式需要返回服務的名稱。否則,認為你傳遞的是一個建構函式,呼叫建構函式會返回服務例項物件。
——————————————————————————————————————————————————————————————————————
factory(name, $getFunction()) | 一個不可配置的服務,需要你指定一個函式,當呼叫這個函式的時候,會返回服務的例項。
| 可以用把它看成 provider (name, { $get: $getFunction()}) 的形式
——————————————————————————————————————————————————————————————————————
service(name, constructor()) | 一個不可配置的服務,與上面 provider 函式的constructor 引數類似,呼叫它可以建立服務例項
——————————————————————————————————————————————————————————————————————
下面用 factory() 的方式編寫服務:
1 // 建立一個模型用來支撐我們的購物檢視 2 var shoppingModule = angular.module('ShoppingModule', []); 3 4 // 設定好服務工廠,用來建立我們的 Items 介面,以便訪問服務端資料庫 5 shoppingModule.factory('Items', function () { 6 var items = {}; 7 items.query = function () { 8 return [ 9 {title: 'Paint', description: 'Pots full of paint', price: 3.95}, 10 {title: 'Polka', description: 'Dots with polka', price: 2.95}, 11 {title: 'Pebbles', description: 'Just little rocks', price: 6.95} 12 ]; 13 }; 14 return items; 15 });
控制器就很簡單了,但是需要注入 Items 物件:
1 shoppingModule.controller('ShoppingController', function ($scope, Items) { 2 $scope.items = Items.query(); 3 });
頁面可以寫個很簡單的如下:
1 <html ng-app="ShoppingModule"> 2 <body ng-controller="ShoppingController"> 3 <script src="../src/angular.js"></script> 4 <script src="factory.js"></script> 5 <h1>Shop!</h1> 6 <table> 7 <tr ng-repeat="item in items"> 8 <td>{{item.title}}</td> 9 <td>{{item.description}}</td> 10 <td>{{item.price | currency}}</td> 11 </tr> 12 </table> 13 </body> 14 </html>
效果如下:
12、使用過濾器格式化資料
你可以用過濾器來宣告應該如何變換資料格式,然後再顯示給使用者,只要在模板中使用一個插值變數即可,過濾器的語法如下:
{{ expression | filterName : parameter1 : ... parameterN}}
Angular 的內建過濾器,比如 currency:
{{12.9 | currency}}
顯示結果為:
$12.90
還有其他過濾器,比如 date、number、uppercase 等,可以連用:
{{12.9 | currency | number : 0}}
顯示結果為:
$13
當然,我們經常需要自定義過濾器,比如我們需要為標題文字建立首字母大寫的字串,編寫過濾器如下:
1 var homeModule = angular.module('HomeModule', []); 2 3 homeModule.filter('titleCase', function () { 4 return function (input) { 5 var words = input.split(' '); 6 for (var i = 0; i < words.length; i++) { 7 words[i] = words[i].charAt(0).toUpperCase() + words[i].slice(1); 8 } 9 return words.join(' '); 10 }; 11 }); 12 13 homeModule.controller('HomeController', function ($scope) { 14 $scope.pageHeading = 'behold the majesty of your page title'; 15 });
簡單的模板頁面:
1 <html> 2 <body ng-app="HomeModule" ng-controller="HomeController"> 3 <script src="../src/angular.js"></script> 4 <script src="filter.js"></script> 5 <h1>{{pageHeading | titleCase}}</h1> 6 </body> 7 </html>
顯示結果如下: