足以给应用绑定了硬编码数据集中的三条手机数据,让我们从我们的服务中适配更大的数据集,他使用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