AngularJS入门教程之XHR和依赖注入详解


Posted in Javascript onAugust 18, 2016

到现在为止,我们使用是硬编码的三条手机记录数据集。现在我们使用AngularJS一个内置服务$http来获取一个更大的手机记录数据集。我们将使用AngularJS的依赖注入(dependency injection (DI))功能来为PhoneListCtrl控制器提供这个AngularJS服务。

请重置工作目录:

git checkout -f step-5

刷新浏览器,你现在应该能看到一个20部手机的列表。

步骤4和步骤5之间最重要的不同在下面列出。你可以在GitHub里看到完整的差别。

数据

你项目当中的app/phones/phones.json文件是一个数据集,它以JSON格式存储了一张更大的手机列表。

下面是这个文件的一个样例:

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

控制器

我们在控制器中使用AngularJS服务$http向你的Web服务器发起一个HTTP请求,以此从app/phones/phones.json文件中获取数据。$http仅仅是AngularJS众多内建服务中之一,这些服务可以处理一些Web应用的通用操作。AngularJS能将这些服务注入到任何你需要它们的地方。

服务是通过AngularJS的依赖注入DI子系统来管理的。依赖注入服务可以使你的Web应用良好构建(比如分离表现层、数据和控制三者的部件)并且松耦合(一个部件自己不需要解决部件之间的依赖问题,它们都被DI子系统所处理)。

app/js/controllers.js

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

 $scope.orderProp = 'age';
}

$http向Web服务器发起一个HTTP GET请求,索取phone/phones.json(注意,url是相对于我们的index.html文件的)。服务器用json文件中的数据作为响应。(这个响应或许是实时从后端服务器动态产生的。但是对于浏览器来说,它们看起来都是一样的。为了简单起见,我们在教程里面简单地使用了一个json文件。)

$http服务用success返回[对象应答][ng.$q]。当异步响应到达时,用这个对象应答函数来处理服务器响应的数据,并且把数据赋值给作用域的phones数据模型。注意到AngularJS会自动检测到这个json应答,并且已经为我们解析出来了!

为了使用AngularJS的服务,你只需要在控制器的构造函数里面作为参数声明出所需服务的名字,就像这样:

function PhoneListCtrl($scope, $http) {...}

当控制器构造的时候,AngularJS的依赖注入器会将这些服务注入到你的控制器中。当然,依赖注入器也会处理所需服务可能存在的任何传递性依赖(一个服务通常会依赖于其他的服务)。

注意到参数名字非常重要,因为注入器会用他们去寻找相应的依赖。

AngularJS入门教程之XHR和依赖注入详解

'$'前缀命名习惯

你可以创建自己的服务,实际上我们在步骤11就会学习到它。作为一个命名习惯,AngularJS内建服务,作用域方法,以及一些其他的AngularJS API都在名字前面使用一个‘$'前缀。不要使用‘$'前缀来命名你自己的服务和模型,否则可能会产生名字冲突。

关于JS压缩

由于AngularJS是通过控制器构造函数的参数名字来推断依赖服务名称的。所以如果你要压缩PhoneListCtrl控制器的JS代码,它所有的参数也同时会被压缩,这时候依赖注入系统就不能正确的识别出服务了。

为了克服压缩引起的问题,只要在控制器函数里面给$inject属性赋值一个依赖服务标识符的数组,就像被注释掉那段最后一行那样:

PhoneListCtrl.$inject = ['$scope', '$http'];

另一种方法也可以用来指定依赖列表并且避免压缩问题——使用Javascript数组方式构造控制器:把要注入的服务放到一个字符串数组(代表依赖的名字)里,数组最后一个元素是控制器的方法函数:

var PhoneListCtrl = ['$scope', '$http', function($scope, $http) { /* constructor body */ }];

上面提到的两种方法都能和AngularJS可注入的任何函数完美协作,要选哪一种方式完全取决于你们项目的编程风格,建议使用数组方式。

测试

test/unit/controllerSpec.js:

由于我们现在开始使用依赖注入,并且我们的控制器也含有了许多依赖服务,所以为我们的控制器构造测试就有一点小小的复杂了。我们需要使用new操作并且提供给构造器包括$http的一些伪实现。然而,我们推荐的方法(而且更加简单噢)是在测试环境下创建一个控制器,使用的方法和AngularJS在产品代码于下面的场景下做的一样:

describe('PhoneCat controllers', function() {

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

注意:因为我们在测试环境中加载了Jasmine和angular-mock.js,我们有了两个辅助方法,module和inject,来帮助我们获得和配置注入器。

用如下方法,我们在测试环境中创建一个控制器:

我们使用inject方法将$rootScope,$controller和$httpBackend服务实例注入到Jasmine的beforeEach函数里。这些实例都来自一个注入器,但是这个注入器在每一个测试内部都会被重新创建。这样保证了每一个测试都从一个周知的起始点开始,并且每一个测试都和其他测试相互独立。

调用$rootScope.$new()来为我们的控制器创建一个新的作用域。

PhoneListCtrl函数和刚创建的作用域作为参数,传递给已注入的$controller函数。

由于我们现在的代码在创建PhoneListCtrl子作用域之前,于控制器中使用$http服务获取了手机列表数据,我们需要告诉测试套件等待一个从控制器来的请求。我们可以这样做:

将请求服务$httpBackend注入到我们的beforeEach函数中。这是这个服务的一个伪版本,这样做在产品环境中有助于处理所有的XHR和JSONP请求。服务的伪版本允许你不用考虑原生API和全局状态——随便一个都能构成测试的噩梦——就可以写测试。

使用$httpBackend.expectGET方法来告诉$httpBackend服务来等待一个HTTP请求,并且告诉它如何对其进行响应。注意到,当我们调用$httpBackend.flush方法之前,响应是不会被发出的。

现在,

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()来清空(flush)请求队列。这样会使得$http服务返回的promise(什么是promise请参见这里)能够被解释成规范的应答。

我们设置一些断言,来验证手机数据模型已经在作用域里了。

最终,我们验证orderProp的默认值被正确设置:

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

练习

在index.html末尾添加一个{{phones | json}}绑定,观察json格式的手机列表。

在PhoneListCtrl控制器中,把HTTP应答预处理一下,使得只显示手机列表的前五个。在$http回调函数里面使用如下代码:

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

总结

现在你应该感觉得到使用AngularJS的服务是多么的容易(这都要归功于AngularJS服务的依赖注入机制),转到步骤6,你会为手机添加缩略图和链接。

谢谢大家对本站的支持,后续继续更新相关文章!

Javascript 相关文章推荐
js鼠标点击事件在各个浏览器中的写法及Event对象属性介绍
Jan 24 Javascript
jQuery阻止事件冒泡具体实现
Oct 11 Javascript
JavaScript实现页面5秒后自动跳转的方法
Apr 16 Javascript
谈谈Jquery中的children find 的区别有哪些
Oct 19 Javascript
谈谈我对JavaScript原型和闭包系列理解(随手笔记8)
Dec 24 Javascript
基于jQuery1.9版本如何判断浏览器版本类型
Jan 12 Javascript
js的函数的按值传递参数(实例讲解)
Nov 16 Javascript
Phaser.js实现简单的跑酷游戏附源码下载
Oct 26 Javascript
详解如何快速配置webpack多入口脚手架
Dec 28 Javascript
layui递归实现动态左侧菜单
Jul 26 Javascript
vue实现树状表格效果
Dec 29 Vue.js
聊聊vue 中的v-on参数问题
Jan 29 Vue.js
JavaScript中函数声明与函数表达式的区别详解
Aug 18 #Javascript
Javascript中apply、call、bind的巧妙使用
Aug 18 #Javascript
AngularJS入门教程之双向绑定详解
Aug 18 #Javascript
AngularJS入门教程之迭代器过滤详解
Aug 18 #Javascript
AngularJS入门教程之AngularJS 模板
Aug 18 #Javascript
AngularJS入门教程之静态模板详解
Aug 18 #Javascript
AngularJS入门教程引导程序
Aug 18 #Javascript
You might like
刚才在简化php的库,结果发现很多东西
2006/12/31 PHP
php采集速度探究总结(原创)
2008/04/18 PHP
使用PHP会话(Session)实现用户登陆功能
2013/06/29 PHP
PHP判断上传文件类型的解决办法
2015/10/20 PHP
laravel5.6框架操作数据curd写法(查询构建器)实例分析
2020/01/26 PHP
JavaScript实现点击按钮后变灰避免多次重复提交
2013/07/15 Javascript
jQuery Animation实现CSS3动画示例介绍
2013/08/14 Javascript
纯JS实现动态时间显示代码
2014/02/08 Javascript
js实现对table动态添加、删除和更新的方法
2015/02/10 Javascript
js弹出对话框方式小结
2015/11/17 Javascript
全面解析Bootstrap中scrollspy(滚动监听)的使用方法
2016/06/06 Javascript
easyui导出excel无法弹出下载框的快速解决方法
2016/11/10 Javascript
微信小程序实现页面跳转传值的方法
2017/10/12 Javascript
vux uploader 图片上传组件的安装使用方法
2018/05/15 Javascript
解决jquery的ajax调取后端数据成功却渲染失败的问题
2018/08/08 jQuery
解决jQuery使用append添加的元素事件无效的问题
2018/08/30 jQuery
nodejs基础之buffer缓冲区用法分析
2018/12/26 NodeJs
深入浅出了解Node.js Streams
2019/05/27 Javascript
用webpack4开发小程序的实现方法
2019/06/04 Javascript
Node.js HTTP服务器中的文件、图片上传的方法
2019/09/23 Javascript
Vue使用虚拟dom进行渲染view的方法
2019/12/26 Javascript
在JavaScript中查找字符串中最长单词的三种方法(推荐)
2021/01/18 Javascript
[13:18]《一刀刀一天》之DOTA全时刻21:详解TI新赛制 A队再露獠牙
2014/06/24 DOTA
python str与repr的区别
2013/03/23 Python
基于python元祖与字典与集合的粗浅认识
2017/08/23 Python
Python3 集合set入门基础
2020/02/10 Python
Python 实现将大图切片成小图,将小图组合成大图的例子
2020/03/14 Python
中兴通讯全球官方网站:ZTE
2020/12/26 全球购物
会计学专业学生的求职信范文
2014/01/27 职场文书
鲜花方阵解说词
2014/02/13 职场文书
汽车销售员如何做职业生涯规划
2014/02/16 职场文书
学生上课说话检讨书
2014/10/25 职场文书
个人整改措施落实情况汇报
2014/10/29 职场文书
读《人生的智慧》有感:闲暇是人生的精华
2019/12/25 职场文书
python 利用 PIL 将数组值转成图片的实现
2021/04/12 Python
帮你提高开发效率的JavaScript20个技巧
2021/06/18 Javascript