5-XHRs(XmlHttpRequest)與依賴注入

足以給應用綁定了硬編碼數據集中的三條手機數據,讓咱們從咱們的服務中適配更大的數據集,他使用Angular內建的 服務$htttp。咱們將使用Angular的 依賴注入PhoneListCtrl提供服務。 如今有20條手機,從服務器中讀取。 工做空間重置介紹 重置你的工做區間到第五步 html

git checkout -f step-5

刷新你的瀏覽器,或者在線上檢出這一步:第五步例子 大部分重要的修改都列在下面,你在 GitHub上能看所有不一樣。git

數據

app/phones/phones.json文件在你的項目中是一個數據集,他包含一個用JONS格式存儲的手機大列表。 下面是簡單的文件angularjs

[
 {
  "age": 13,
  "id": "motorola-defy-with-motoblur",
  "name": "Motorola DEFY\u2122 with MOTOBLUR\u2122",
  "snippet": "Are you ready for everything life throws your way?"
  ...
 },
...
]

控制器

咱們使用Angular的 $http服務,在咱們控制器中製做HTTP請求來從 app/phones/phones.json文件中讀取數據。$http是一系列 Angular內建的服務,他在web應用中處理相同的操做,Angular在你須要的地方注入這些服務。 服務由Angular的 DI子系統管理,依賴注入幫助你構建應用能既有良好的結構(像描述,數據,控制控件操做)和鬆耦合(組件之間的依賴不是組件本身,而是由依賴注入了系統解決。) app/js/controllers.js:github

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

phonecatApp.controller('PhoneListCtrl', function ($scope, $http) {
  $http.get('phones/phones.json').success(function(data) {
    $scope.phones = data;
  });

  $scope.orderProp = 'age';
});

$http製造一個HTTP GET 請求去咱們Web服務器,請求 phones/phones.json(這個url放在咱們的index.html頁面中),服務器響應json文件中的數據(這個響應多是後端服務器動態生成,瀏覽器和咱們的應用看到的是同樣,在教程爲了簡單的目的咱們使用json文件) $http服務返用成功方法返回一個 Promise對象,對於phones模型,咱們調用這個方法去處理異步響應,經過控制器將手機數據賦給scope。注意Angular檢測json響應,解析給咱們。 爲了使用Angular中的服務,你簡單定義了依賴的名字,作爲控制器的構造器的參數,如:web

phonecatApp.controller('PhoneListCtrl', function ($scope, $http) {...}

當控制器被構建時,Angular的依賴注入器提供服務給你的控制器,依賴注入器也當心地建立服務可能有的過渡依賴(服務可能依賴其餘服務)。 注意參數的名字很重要,由於註冊器用他們尋找依賴。json

$前綴命名規定

你能夠建立本身的服務,實際上咱們剛好在作第11步的事,作爲命名規定,Angular的內建服務,範圍方法和其餘一些Angular 應用接口在名字前有$前綴。 $前綴在這裏是Angular提供服務的名字空間,爲了防止衝突,最好是避免你的服務和模型其餘東西用 $開關。 若是你檢查一個範圍,你可能注意有些屬性是用 $$開關,這些屬性是被認爲是私有,不能被讀取或修改。後端

注意最小化倍率

當Angular從控制器構建方法的參數名字中推導控制器的依賴,若是你 壓縮 PhoneListCtrl的JavaScript代碼,全部的方法參數都會壓縮良好,依賴注入器可能不能正確識別服務。 咱們能用依賴的名字的註解來避免這個問題,註解提供的字符串不會被最小化,這裏有兩種方法提供注入註解。api

  • 在控制器中,建立一個持有一個字符數組的$inject屬性,數組中的每一個字符串是對應於要注入服務的參數的名字,在咱們的例子中能夠這麼寫:數組

    function PhoneListCtrl($scope, $http) {...}
    PhoneListCtrl.$inject = ['$scope', '$http'];
    phonecatApp.controller('PhoneListCtrl', PhoneListCtrl);

  • 使用一行註解,代替咱們剛剛提供的方法,你提供一個數組,數組包括服務名稱的列表,後面跟着函數:promise

    function PhoneListCtrl($scope, $http) {...}
    phonecatApp.controller('PhoneListCtrl', ['$scope', '$http', PhoneListCtrl]);

兩種方法均可以經過Angular給任何函數注入,因此選擇那個決定於你的工程風格。 當咱們使用第二種方法,經常使用方法是當注入控制器時在一行內提供匿名函數構建器。

phonecatApp.controller('PhoneListCtrl', ['$scope', '$http', function($scope, $http) {...}]);

從這點出發,咱們將在教程中使用行內方法,用這種意圖,讓咱們增長一個註解給咱們的 PhoneListCtrl app/js/controllers.js:

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

phonecatApp.controller('PhoneListCtrl', ['$scope', '$http',
  function ($scope, $http) {
    $http.get('phones/phones.json').success(function(data) {
      $scope.phones = data;
    });

    $scope.orderProp = 'age';
  }]);

測試

test/unit/controllersSpec.js: 由於咱們開始使用依賴注入,而且咱們的控制器有了依賴,構造控制器在咱們測試中有一點複雜,咱們使用new操做符,提供有某些假$http實現的控制器,可是 Angular提供模擬$http服務,咱們能用他來作單元測試,咱們配置「假」響應服務經過調用服務上的方法,服務響應。

describe('PhoneCat controllers', function() {

describe('PhoneListCtrl', function(){
  var scope, ctrl, $httpBackend;

  // Load our app module definition before each test.
  beforeEach(module('phonecatApp'));

  // The injector ignores leading and trailing underscores here (i.e. _$httpBackend_).
  // This allows us to inject a service but then attach it to a variable
  // with the same name as the service in order to avoid a name conflict.
  beforeEach(inject(function(_$httpBackend_, $rootScope, $controller) {
    $httpBackend = _$httpBackend_;
    $httpBackend.expectGET('phones/phones.json').
        respond([{name: 'Nexus S'}, {name: 'Motorola DROID'}]);

    scope = $rootScope.$new();
    ctrl = $controller('PhoneListCtrl', {$scope: scope});
  }));

注意:由於在咱們的測試環境中讀取Jasmin和angular-mocks.js,咱們用兩個幫助方法 moduleinject來讀取各配置注入器。 咱們在測試環境中建立控制器,以下:

  • 咱們使用inject幫助方法去注入$rootScope,$controller$httpBackend服務實例給Jasmine的beforeEach函數,這些實例來自注入器,每一個單個測試都會被從新建立,這樣確實每一個測試開始於好的開始點,每一個測試工做時與其餘測試都隔離開。

  • 咱們建立一箇中$rootScope.$new()的新範圍給咱們的控制器。

  • 咱們調用注入的$controller函數,經過名叫PhoneListCtrl的控制器,並建立一個scope作爲參數。

由於咱們在控制器中使用了 $http服務讀取數據,在咱們建立PhoneListCtrl子scope以前,咱們告訴測試從控制器中駕馭一個指望的請求,咱們這樣作:

  • 請求$httpBackend服務注入到beforeEach函數,模擬版本的服務在生產環境能使XHR和JSONP請求更容易,模擬版本的服務容許你在沒有本地API和全局狀態關聯狀況下寫測試,他們兩個都讓測試成爲噩夢。

  • 使用$httpBackend.expectGET方法讓$httpBackend服務期待Http請求到來,並告訴他怎麼返回,注意響應直到調用$httpBackend.flush方法後纔會返回。

如今讓咱們建立一個斷言驗證phones模型在響應接到以前不存在於scope中。

it('should create "phones" model with 2 phones fetched from xhr', function() {
  expect(scope.phones).toBeUndefined();
  $httpBackend.flush();

  expect(scope.phones).toEqual([{name: 'Nexus S'},
                               {name: 'Motorola DROID'}]);
});

  • 在瀏覽器中,咱們經過調用$httpBackend.flush()刷入請求隊列,這樣經過$http服務用準備好的response產生promise的返回值。在mock $httpBackend文檔中看‘刷入Http請求’,解釋爲何這是必須的。

  • 咱們建立斷言,驗證手機模型如今存在有scope中。

最後,咱們驗證orderProp默認值設定正確。

it('should set the default value of orderProp model', function() {
  expect(scope.orderProp).toBe('age');
});

如今你能夠在Karma選項卡中查看下面的輸出。

Chrome 22.0: Executed 2 of 2 SUCCESS (0.028 secs / 0.007 secs)

實驗

在index.html底部,增長 <pre>{{phones | filter:query | orderBy:orderProp | json}}</pre>綁定去看json格式的手機列表。 在PhoneListCtrl控制器中,經過限制列表第一次手機的數目到5來預處理HTTP響應。在回調時,使用下面的代碼。

$scope.phones = data.splice(0, 5);

總結

如今你已經學了使用Angular服務是多麼容易(謝謝Angular的依賴注入),到 第6步,你將給手機增長一些縮略圖和連接。 原文地址: https://docs.angularjs.org/tutorial/step_05

相關文章
相關標籤/搜索