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 相关文章推荐
json跟xml的对比分析
Jun 10 Javascript
jquery及原生js获取select下拉框选中的值示例
Oct 25 Javascript
Javascript 浮点运算精度问题分析与解决
Mar 26 Javascript
浅析JavaScript中命名空间namespace模式
Jun 22 Javascript
AngularJs基本特性解析(一)
Jul 21 Javascript
JS简单获取及显示当前时间的方法
Aug 03 Javascript
vue.js+Element实现表格里的增删改查
Jan 18 Javascript
HTML的select控件美化
Mar 27 Javascript
JS表格组件神器bootstrap table使用指南详解
Apr 12 Javascript
React Native第三方平台分享的实例(Android,IOS双平台)
Aug 04 Javascript
深入理解 webpack 文件打包机制(小结)
Jan 08 Javascript
javaScript实现游戏倒计时功能
Nov 17 Javascript
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
初学者入门:细述PHP4的核心Zend
2006/09/05 PHP
PHP防止跨域提交表单
2013/11/01 PHP
php获取字段名示例分享
2014/03/03 PHP
Fedora下安装php Redis扩展笔记
2014/09/03 PHP
php字符比较函数similar_text、strnatcmp与strcasecmp用法分析
2014/11/18 PHP
PHP使用正则表达式实现过滤非法字符串功能示例
2018/06/04 PHP
jQuery formValidator表单验证插件开源了 含API帮助、源码、示例
2008/08/14 Javascript
javascript实现tabs选项卡切换效果(扩展版)
2013/03/19 Javascript
javascript对浅拷贝和深拷贝的详解
2016/10/14 Javascript
动态加载css方法实现和深入解析
2017/01/18 Javascript
node.js+jQuery实现用户登录注册AJAX交互
2017/04/28 jQuery
vue2中的keep-alive使用总结及注意事项
2017/12/21 Javascript
详解angular2如何手动点击特定元素上的点击事件
2018/10/16 Javascript
详解React 的几种条件渲染以及选择
2018/10/23 Javascript
解决vue admin element noCache设置无效的问题
2019/11/12 Javascript
[04:09]2014DOTA2国际邀请赛Ti西雅图 历届冠军相继出局 BBC综述今日比赛
2014/07/20 DOTA
使用PYTHON接收多播数据的代码
2012/03/01 Python
5种Python单例模式的实现方式
2016/01/14 Python
Python 数据库操作 SQLAlchemy的示例代码
2019/02/18 Python
Python自动化之数据驱动让你的脚本简洁10倍【推荐】
2019/06/04 Python
python 列表输出重复值以及对应的角标方法
2019/06/11 Python
Django框架之登录后自定义跳转页面的实现方法
2019/07/18 Python
使用python制作一个解压缩软件
2019/11/13 Python
使用TensorFlow对图像进行随机旋转的实现示例
2020/01/20 Python
python+django+selenium搭建简易自动化测试
2020/08/19 Python
巴西食品补充剂在线零售商:Músculos na Web
2017/08/07 全球购物
新闻学专业应届生求职信
2013/11/08 职场文书
物业管理计划书
2014/01/10 职场文书
煤矿安全演讲稿
2014/05/09 职场文书
小学校园文化建设汇报材料
2014/08/19 职场文书
落实八项规定专题民主生活会对照检查材料
2014/09/15 职场文书
中级会计大学生职业生涯规划书
2014/09/16 职场文书
党员示范岗材料
2014/12/19 职场文书
单位介绍信格式
2015/01/31 职场文书
电力安全学习心得体会
2016/01/18 职场文书
Python基础之Socket通信原理
2021/04/22 Python