5-XHRs(XmlHttpRequest)与依赖注入

足以给应用绑定了硬编码数据集中的三条手机数据,让我们从我们的服务中适配更大的数据集,他使用Angular内建的 服务中 $htttp。我们将使用Angular的 依赖注入给 PhoneListCtrl提供服务。 现在有20条手机,从服务器中读取。 工作空间重置介绍 重置你的工作区间到第五步

git checkout -f step-5

刷新你的浏览器,或者在线上检出这一步:第五步例子。 大部分重要的修改都列在下面,你在 GitHub上能看全部不同。

数据

app/phones/phones.json文件在你的项目中是一个数据集,他包含一个用JONS格式存储的手机大列表。 下面是简单的文件

[
 {
  "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:

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中的服务,你简单定义了依赖的名字,做为控制器的构造器的参数,如:

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

当控制器被构建时,Angular的依赖注入器提供服务给你的控制器,依赖注入器也小心地创建服务可能有的过渡依赖(服务可能依赖其他服务)。 注意参数的名字很重要,因为注册器用他们寻找依赖。

$前缀命名规定

你可以创建自己的服务,实际上我们恰好在做第11步的事,做为命名规定,Angular的内建服务,范围方法和其他一些Angular 应用接口在名字前有$前缀。 $前缀在这里是Angular提供服务的名字空间,为了防止冲突,最好是避免你的服务和模型其他东西用 $开关。 如果你检查一个范围,你可能注意有些属性是用 $$开关,这些属性是被认为是私有,不能被读取或修改。

注意最小化倍率

当Angular从控制器构建方法的参数名字中推导控制器的依赖,如果你 压缩 PhoneListCtrl的JavaScript代码,所有的方法参数都会压缩良好,依赖注入器可能不能正确识别服务。 我们能用依赖的名字的注解来避免这个问题,注解提供的字符串不会被最小化,这里有两种方法提供注入注解。

  • 在控制器中,创建一个持有一个字符数组的$inject属性,数组中的每个字符串是对应于要注入服务的参数的名字,在我们的例子中可以这么写:

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

  • 使用一行注解,代替我们刚刚提供的方法,你提供一个数组,数组包括服务名称的列表,后面跟着函数:

    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,我们用两个帮助方法 module和 inject来读取各配置注入器。 我们在测试环境中创建控制器,如下:

  • 我们使用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

你可能感兴趣的:(5-XHRs(XmlHttpRequest)与依赖注入)