AngularJS REST和自定義服務(wù)

2022-04-15 14:37 更新

REST和自定義服務(wù)

在這一步中,你將改變我們獲取數(shù)據(jù)的方法。

  • 我們定義了一個自定義服務(wù),它代表了一個RESTful客戶端。利用該客戶端,我們可以用更容易的方式制作一個向服務(wù)器索取數(shù)據(jù)的請求,不需要去處理底層?$http API、HTTP方法以及URL。

把工作空間重置到第十一步

git checkout -f step-11

刷新你的瀏覽器或在線檢查這一步:Step 8 Live Demo

下面列出了第十步和第十一步之間最重要的區(qū)別。你可以在GitHub上看到完整的差異。

依賴性

Angular在ngResource模塊中提供了安靜的功能,它是與核心Angular框架分開分布的。

我們正在使用Bower以安裝客戶端依賴性。這一步更新的bower.json配置文件,以包含新的依賴性:

{
  "name": "angular-seed",
  "description": "A starter project for AngularJS",
  "version": "0.0.0",
  "homepage": "https://github.com/angular/angular-seed",
  "license": "MIT",
  "private": true,
  "dependencies": {
    "angular": "1.4.x",
    "angular-mocks": "1.4.x",
    "jquery": "~2.1.1",
    "bootstrap": "~3.1.1",
    "angular-route": "1.4.x",
    "angular-resource": "1.4.x"
  }
}

新的依賴性"angular-resource": "1.4.x"告訴bower安裝一個以angular為源的組件的版本,它與v1.4x版兼容。我們必須要求bower下載并安裝這個依賴性。我們可以通過運行下面的指令來做到它:

npm install
**警告:**如果自從你上一次運行`npm install`以后,Angular又發(fā)布了一個新版本,則你用`bower install`可能遇到問題,因為你安裝的angular.js的版本與它有沖突。如果你想通過它,則需要在運行`npm install`之前先刪除你的`app/bower_components`文件夾。
**注意:**如果你已經(jīng)全局安裝了bower,則你可以運行`bower install`,但是為了我們已經(jīng)預(yù)配置的項目,`npm install`為我們運行了bower。

模板

我們的自定義源服務(wù)將被定義在app/js/services.js中,因此我們需要在我們的布局模板中包含這個文件。另外,我們還需要載入angular-resouces.js文件,它包含了ngResource模塊:

app/index.html.

...
  <script src="/attachments/image/wk/angularjs/angular-resource.js"></script>
  <script src="/attachments/image/wk/angularjs/services.js"></script>
...

服務(wù)

我們創(chuàng)建了自己的服務(wù),以提供對服務(wù)器上的手機數(shù)據(jù)的訪問:

app/js/services.js.

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

phonecatServices.factory('Phone', ['$resource',
  function($resource){
    return $resource('phones/:phoneId.json', {}, {
      query: {method:'GET', params:{phoneId:'phones'}, isArray:true}
    });
  }]);

我們使用模塊API,利用工廠函數(shù)注冊自定義的服務(wù)。我們傳入服務(wù)的名稱“Phone”以及工廠函數(shù)。工廠函數(shù)的結(jié)構(gòu)近似于控制器,兩者都可以聲明依賴性,以通過函數(shù)參數(shù)注入。Phone服務(wù)在$resource服務(wù)上聲明了一個依賴性。

$resource服務(wù)使它更容易只用寥寥幾行代碼創(chuàng)建一個RESTful客戶端。這種客戶端可以用在我們的應(yīng)用中,代替底層$http服務(wù)。

app/js/app.js.

...
angular.module('phonecatApp', ['ngRoute', 'phonecatControllers','phonecatFilters', 'phonecatServices']).
...

我們需要把phonecatServices模塊依賴性添加到phonecatApp模塊的需要數(shù)列中。

控制器

通過重構(gòu)掉底層的$http服務(wù),我們簡化了我們的子控制器(PhoneListCtrlPhoneDetailCtrl),用稱為Phone的服務(wù)替代它。Angular的$resource服務(wù)比$http更容易使用,用來與作為REST的源對外提供的數(shù)據(jù)源交互?,F(xiàn)在我們更容易理解控制器中的這些代碼是干什么的了。

app/js/controllers.js.

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

...

phonecatControllers.controller('PhoneListCtrl', ['$scope', 'Phone', function($scope, Phone) {
  $scope.phones = Phone.query();
  $scope.orderProp = 'age';
}]);

phonecatControllers.controller('PhoneDetailCtrl', ['$scope', '$routeParams', 'Phone', function($scope, $routeParams, Phone) {
  $scope.phone = Phone.get({phoneId: $routeParams.phoneId}, function(phone) {
    $scope.mainImageUrl = phone.images[0];
  });

  $scope.setImage = function(imageUrl) {
    $scope.mainImageUrl = imageUrl;
  }
}]);

注意我們把PhoneList內(nèi)部替換成了什么:

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

換成:

$scope.phones = Phone.query();

我們通過這條簡單語句來查詢所有手機。

一個需要注意的重要事情是,在上面的代碼中,在引用手機服務(wù)的方法的時候,我們沒有傳遞任何回調(diào)函數(shù)。雖然它看起來就像結(jié)果是同步返回的,但其實根本不是。同步返回的是一個“future”——一個對象,當(dāng)XHR響應(yīng)返回的時候,將填入數(shù)據(jù)。因為Angular中的數(shù)據(jù)綁定,我們可以使用這個future并且把它綁定到我們的模板上。然后,當(dāng)數(shù)據(jù)到達的時候,視圖將自動更新。

有些時候,單憑future對象和數(shù)據(jù)綁定不足以滿足我們所有的需求,在那種情況下,我們可以添加一個回調(diào)函數(shù),以處理服務(wù)器響應(yīng)。PhoneDetailCtrl控制器通過設(shè)置回調(diào)函數(shù)中的mainImageUrl來演示它。

測試

因為我們現(xiàn)在使用了ngResource模塊,為了用以angular為源更新Karma配置單文件,它是必要的,這樣新測試才能通過。

test/karma.conf.js:

    files : [
      'app/bower_components/angular/angular.js',
      'app/bower_components/angular-route/angular-route.js',
      'app/bower_components/angular-resource/angular-resource.js',
      'app/bower_components/angular-mocks/angular-mocks.js',
      'app/js/**/*.js',
      'test/unit/**/*.js'
    ],

我們已經(jīng)修改了我們的單元測試,以驗證我們的新服務(wù)會發(fā)起HTTP請求,并像預(yù)期那樣處理它們。測試還檢查了我們的控制器正確地與服務(wù)交互。

$resource服務(wù)參增加了帶有用來更新和刪除源的方法的響應(yīng)對象。如果我們打算使用標(biāo)準(zhǔn)的toEqual匹配器,我們的測試將失敗,因為測試值不能與響應(yīng)嚴(yán)格匹配。要想解決這個問題,我們使用了一個新定義的toEqualData[Jasmine matcher][jasmine匹配器]。當(dāng)toEqualData匹配器對比兩個對象的時候,它考慮對象屬性屬性而忽略對象方法。

test/unit/controllersSpec.js:

describe('PhoneCat controllers', function() {

  beforeEach(function(){
    this.addMatchers({
      toEqualData: function(expected) {
        return angular.equals(this.actual, expected);
      }
    });
  });

  beforeEach(module('phonecatApp'));
  beforeEach(module('phonecatServices'));

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

    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});
    }));

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

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

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

  describe('PhoneDetailCtrl', function(){
    var scope, $httpBackend, ctrl,
        xyzPhoneData = function() {
          return {
            name: 'phone xyz',
            images: ['image/url1.png', 'image/url2.png']
          }
        };

    beforeEach(inject(function(_$httpBackend_, $rootScope, $routeParams, $controller) {
      $httpBackend = _$httpBackend_;
      $httpBackend.expectGET('phones/xyz.json').respond(xyzPhoneData());

      $routeParams.phoneId = 'xyz';
      scope = $rootScope.$new();
      ctrl = $controller('PhoneDetailCtrl', {$scope: scope});
    }));

    it('should fetch phone detail', function() {
      expect(scope.phone).toEqualData({});
      $httpBackend.flush();

      expect(scope.phone).toEqualData(xyzPhoneData());
    });
  });
});

你現(xiàn)在可以在Karma選項卡中看到如下的輸出:

Chrome 22.0: Executed 5 of 5 SUCCESS (0.038 secs / 0.01 secs)

總結(jié)

現(xiàn)在我們已經(jīng)看到了如何建立一個自定義的服務(wù),作為REST的客戶端,我們已經(jīng)準(zhǔn)備好前往第十二步 應(yīng)用動畫(最后一步)以學(xué)會如何用動畫提高應(yīng)用程序。

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號