AngularJS入门教程之REST和定制服务详解


Posted in Javascript onAugust 19, 2016

在这一步中,我们会改进我们APP获取数据的方式。

请重置工作目录:

git checkout -f step-11

对我们应用所做的最后一个改进就是定义一个代表RESTful客户端的定制服务。有了这个客户端我们可以用一种更简单的方式来发送XHR请求,而不用去关心更底层的$http服务(API、HTTP方法和URL)。

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

模板

定制的服务被定义在app/js/services,所以我们需要在布局模板中引入这个文件。另外,我们也要加载angularjs-resource.js这个文件,它包含了ngResource模块以及其中的$resource服务,我们一会就会用到它们:

app/index.html

...
 <script src="js/services.js"></script>
 <script src="lib/angular/angular-resource.js"></script>
...

服务

app/js/services.js

angular.module('phonecatServices', ['ngResource']).
  factory('Phone', function($resource){
   return $resource('phones/:phoneId.json', {}, {
    query: {method:'GET', params:{phoneId:'phones'}, isArray:true}
   });
  });

我们使用模块API通过一个工厂方法注册了一个定制服务。我们传入服务的名字Phone和工厂函数。工厂函数和控制器构造函数差不多,它们都通过函数参数声明依赖服务。Phone服务声明了它依赖于$resource服务。

$resource服务使得用短短的几行代码就可以创建一个RESTful客户端。我们的应用使用这个客户端来代替底层的$http服务。

app/js/app.js

...
angular.module('phonecat', ['phonecatFilters', 'phonecatServices']).
...

我们需要把phonecatServices添加到phonecat的依赖数组里。

控制器

通过重构掉底层的$http服务,把它放在一个新的服务Phone中,我们可以大大简化子控制器(PhoneListCtrl和PhoneDetailCtrl)。AngularJS的$resource相比于$http更加适合于与RESTful数据源交互。而且现在我们更容易理解控制器这些代码在干什么了。

app/js/controllers.js

...

function PhoneListCtrl($scope, Phone) {
 $scope.phones = Phone.query();
 $scope.orderProp = 'age';
}

//PhoneListCtrl.$inject = ['$scope', 'Phone'];



function PhoneDetailCtrl($scope, $routeParams, Phone) {
 $scope.phone = Phone.get({phoneId: $routeParams.phoneId}, function(phone) {
  $scope.mainImageUrl = phone.images[0];
 });

 $scope.setImage = function(imageUrl) {
  $scope.mainImageUrl = imageUrl;
 }
}

//PhoneDetailCtrl.$inject = ['$scope', '$routeParams', 'Phone'];

注意到,在PhoneListCtrl里我们把:

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

换成了:

$scope.phones = Phone.query();

我们通过这条简单的语句来查询所有的手机。

另一个非常需要注意的是,在上面的代码里面,当调用Phone服务的方法是我们并没有传递任何回调函数。尽管这看起来结果是同步返回的,其实根本就不是。被同步返回的是一个“future”——一个对象,当XHR相应返回的时候会填充进数据。鉴于AngularJS的数据绑定,我们可以使用future并且把它绑定到我们的模板上。然后,当数据到达时,我们的视图会自动更新。

有的时候,单单依赖future对象和数据绑定不足以满足我们的需求,所以在这些情况下,我们需要添加一个回调函数来处理服务器的响应。PhoneDetailCtrl控制器通过在一个回调函数中设置mainImageUrl就是一个解释。

测试

修改我们的单元测试来验证我们新的服务会发起HTTP请求并且按照预期地处理它们。测试同时也检查了我们的控制器是否与服务正确协作。

$resource服务通过添加更新和删除资源的方法来增强响应得到的对象。如果我们打算使用toEqual匹配器,我们的测试会失败,因为测试值并不会和响应完全等同。为了解决这个问题,我们需要使用一个最近定义的toEqualDataJasmine匹配器。当toEqualData匹配器比较两个对象的时候,它只考虑对象的属性而忽略掉所有的方法。

test/unit/controllersSpec.js:

describe('PhoneCat controllers', function() {

 beforeEach(function(){
  this.addMatchers({
   toEqualData: function(expected) {
    return angular.equals(this.actual, expected);
   }
  });
 });

 beforeEach(module('phonecatServices'));

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

  it('should create "phones" model with 2 phones fetched from xhr', function() {
   expect(scope.phones).toEqual([]);
   $httpBackend.flush();

   expect(scope.phones).toEqualData(
     [{name: 'Nexus S'}, {name: 'Motorola DROID'}]);
  });

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

 describe('PhoneDetailCtrl', function(){
  var scope, $httpBackend, ctrl,
    xyzPhoneData = function() {
     return {
      name: 'phone xyz',
      images: ['image/url1.png', 'image/url2.png']
     }
    };

  beforeEach(inject(function(_$httpBackend_, $rootScope, $routeParams, $controller) {
   $httpBackend = _$httpBackend_;
   $httpBackend.expectGET('phones/xyz.json').respond(xyzPhoneData());

   $routeParams.phoneId = 'xyz';
   scope = $rootScope.$new();
   ctrl = $controller(PhoneDetailCtrl, {$scope: scope});
  }));

  it('should fetch phone detail', function() {
   expect(scope.phone).toEqualData({});
   $httpBackend.flush();

   expect(scope.phone).toEqualData(xyzPhoneData());
  });
 });
});

执行./scripts/test.sh运行测试,你应该会看到如下的输出:

Chrome: Runner reset.
....
Total 4 tests (Passed: 4; Fails: 0; Errors: 0) (3.00 ms)
 Chrome 19.0.1084.36 Mac OS: Run 4 tests (Passed: 4; Fails: 0; Errors 0) (3.00 ms)

总结

完工!你在相当短的时间内已经创建了一个Web应用。在完结篇里面我们会提起接下来应该干什么。

以上就是AngularJS RES和定制服务的资料整理,后续继续补充相关资料,希望能帮助大家学习AngularJS!

Javascript 相关文章推荐
jquery+ashx无刷新GridView数据显示插件(实现分页、排序、过滤功能)
Apr 25 Javascript
Jquery实现鼠标移上弹出提示框、移出消失思路及代码
May 19 Javascript
简单的js表单验证函数
Oct 28 Javascript
正则表达式中特殊符号及正则表达式的几种方法总结(replace,test,search)
Nov 26 Javascript
改变隐藏的input中value值的方法
Mar 19 Javascript
在JS数组特定索引处指定位置插入元素
Jul 27 Javascript
jQuery实现网站添加高亮突出显示效果的方法
Jun 26 Javascript
javascript实现别踩白块儿小游戏程序
Nov 22 Javascript
Jquery Easyui进度条组件Progress使用详解(8)
Mar 26 Javascript
ES6新特性之函数的扩展实例详解
Apr 01 Javascript
vue过滤器实现日期格式化的案例分析
Jul 02 Javascript
vue穿梭框实现上下移动
Jan 29 Vue.js
js自调用匿名函数的三种写法(推荐)
Aug 19 #Javascript
AngularJS 入门教程之事件处理器详解
Aug 19 #Javascript
jQuery增加、删除及修改select option的方法
Aug 19 #Javascript
浅谈jquery设置和获得checkbox选中的问题
Aug 19 #Javascript
AngularJS入门教程之过滤器详解
Aug 19 #Javascript
js判断checkbox是否选中个数的方法(超简单)
Aug 19 #Javascript
Angular设置title信息解决SEO方面存在问题
Aug 19 #Javascript
You might like
开发大型 PHP 项目的方法
2007/01/02 PHP
Discuz 模板引擎的封装类代码
2008/07/18 PHP
PHP学习之数组值的操作
2011/04/17 PHP
thinkphp路由规则使用示例详解和伪静态功能实现(apache重写)
2014/02/24 PHP
浅谈PHP中的数据传输CURL
2016/09/06 PHP
Yii框架防止sql注入,xss攻击与csrf攻击的方法
2016/10/18 PHP
PHP中常用的魔术方法
2017/04/28 PHP
PHP抽象类与接口的区别实例详解
2019/05/09 PHP
PHP实现二维数组(或多维数组)转换成一维数组的常见方法总结
2019/12/04 PHP
基于JQuery的模拟苹果桌面Dock效果(稳定版)
2012/10/15 Javascript
无缝滚动改进版支持上下左右滚动(封装成函数)
2012/12/04 Javascript
JS小功能(操作Table--动态添加删除表格及数据)实现代码
2013/11/28 Javascript
jQuery中:gt选择器用法实例
2014/12/29 Javascript
javascript表单事件处理方法详解
2016/05/15 Javascript
JavaScript中全选、全不选、反选、无刷新删除、批量删除、即点即改入库(在yii框架中操作)的代码分享
2016/11/01 Javascript
Bootstrap table使用方法总结
2017/05/10 Javascript
vue项目每30秒刷新1次接口的实现方法
2018/12/04 Javascript
Vue项目中使用jquery的简单方法
2019/05/16 jQuery
通过实例解析js可枚举属性与不可枚举属性
2020/12/02 Javascript
Python+Selenium+PIL+Tesseract自动识别验证码进行一键登录
2017/09/20 Python
使用python爬虫实现网络股票信息爬取的demo
2018/01/05 Python
python使用Tesseract库识别验证
2018/03/21 Python
numpy.ndarray 交换多维数组(矩阵)的行/列方法
2018/08/02 Python
对python中数组的del,remove,pop区别详解
2018/11/07 Python
Nginx+Uwsgi+Django 项目部署到服务器的思路详解
2020/05/08 Python
Python安装Bs4的多种方法
2020/11/28 Python
澳大利亚领先的优质葡萄酒拍卖会:Langton’s Fine Wines
2019/03/24 全球购物
乌克兰鞋类购物网站:Eobuv.com.ua
2020/11/28 全球购物
SQL里面IN比较快还是EXISTS比较快
2012/07/19 面试题
大三在校生电子商务求职信
2013/10/29 职场文书
质量承诺书格式
2014/05/20 职场文书
2014旅游局领导班子四风问题对照检查材料思想汇报
2014/09/19 职场文书
运动会通讯稿100字
2015/07/20 职场文书
教师岗位说明书
2015/09/30 职场文书
python glom模块的使用简介
2021/04/13 Python
CSS 伪元素::marker详解
2021/06/26 HTML / CSS