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 相关文章推荐
jQuery源码分析-01总体架构分析
Nov 14 Javascript
JS下拉框内容左右移动效果的具体实现
Jul 10 Javascript
判断输入是否为空,获得输入类型的JS代码
Oct 30 Javascript
Javascript WebSocket使用实例介绍(简明入门教程)
Apr 16 Javascript
JavaScript正则表达式中的ignoreCase属性使用详解
Jun 16 Javascript
jQuery中attr()与prop()函数用法实例详解(附用法区别)
Dec 29 Javascript
D3.js实现雷达图的方法详解
Sep 22 Javascript
Angular2学习笔记——详解路由器模型(Router)
Dec 02 Javascript
深入了解JavaScript的逻辑运算符(与、或)
Dec 20 Javascript
详解Vue.js分发之作用域槽
Jun 13 Javascript
BootstrapTable加载按钮功能实例代码详解
Sep 22 Javascript
Node.js+ELK日志规范的实现
May 23 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
php xml文件操作代码(一)
2009/03/20 PHP
php摘要生成函数(无乱码)
2012/02/04 PHP
PHP基础陷阱题(变量赋值)
2012/09/12 PHP
浅谈web上存漏洞及原理分析、防范方法(安全文件上存方法)
2013/06/29 PHP
php 批量替换程序的具体实现代码
2013/10/04 PHP
php打印一个边长为N的实心和空心菱型的方法
2015/03/02 PHP
php表单文件iframe异步上传实例讲解
2017/07/26 PHP
jquery索引在使用中的一些困惑
2013/10/24 Javascript
jquery ajax属性async(同步异步)示例
2013/11/05 Javascript
jQuery Validate 验证,校验规则写在控件中的具体实例
2014/02/27 Javascript
JavaScript中一个奇葩的IE浏览器判断方法
2014/04/16 Javascript
js判断游览器类型及版本号的代码
2014/05/11 Javascript
js 判断图片是否加载完以及实现图片的预下载
2014/08/14 Javascript
jQuery事件绑定on()、bind()与delegate() 方法详解
2015/06/03 Javascript
js实现商品抛物线加入购物车特效
2020/11/18 Javascript
js 文字超出长度用省略号代替,鼠标悬停并以悬浮框显示实例
2016/12/06 Javascript
js代码延迟一定时间后执行一个函数的实例
2017/02/15 Javascript
Swiper实现轮播图效果
2017/07/03 Javascript
JS一次前端面试经历记录
2020/03/19 Javascript
[58:35]OG vs EG 2019国际邀请赛淘汰赛 胜者组 BO3 第二场 8.22
2019/09/05 DOTA
Python程序员鲜为人知但你应该知道的17个问题
2014/06/04 Python
django框架自定义用户表操作示例
2018/08/07 Python
python批量获取html内body内容的实例
2019/01/02 Python
Python字符串逆序的实现方法【一题多解】
2019/02/18 Python
Django 对象关系映射(ORM)源码详解
2019/08/06 Python
Python插入Elasticsearch操作方法解析
2020/01/19 Python
更新升级python和pip版本后不生效的问题解决
2020/04/17 Python
使用SQLAlchemy操作数据库表过程解析
2020/06/10 Python
Debenhams百货英国官方网站:Debenhams UK
2016/07/12 全球购物
英国度假别墅预订:Sykes Cottages
2017/06/12 全球购物
Halston Heritage官网:简洁的日装,稍显奢华的晚装
2018/11/20 全球购物
Everything But Water官网:美国泳装品牌
2019/03/17 全球购物
实习自我鉴定
2013/12/15 职场文书
化妆品活动策划方案
2014/05/23 职场文书
2015年党总支工作总结
2015/05/25 职场文书
Windows11性能真的上涨35%? 桌面酷睿i9实测结果公开
2021/11/21 数码科技