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 相关文章推荐
ext for eclipse插件安装方法
Apr 27 Javascript
javascript网页关键字高亮代码
Jul 30 Javascript
js escape,unescape解决中文乱码问题的方法
May 26 Javascript
用jquery存取照片的具体实现方法
Jun 30 Javascript
JavaScript将XML转成JSON的方法
Mar 12 Javascript
jQuery遮罩层实现方法实例详解(附遮罩层插件)
Dec 08 Javascript
js判断出两个字符串最大子串的函数实现方法
Nov 01 Javascript
微信小程序 页面跳转事件绑定的实例详解
Sep 20 Javascript
详解layui中的树形关于取值传值问题
Jan 16 Javascript
vue-cli3.0如何使用CDN区分开发、生产、预发布环境
Nov 22 Javascript
详解mpvue开发微信小程序基础知识
Sep 23 Javascript
weui中的picker使用js进行动态绑定数据问题
Nov 06 Javascript
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获取文件名后缀
2013/06/09 PHP
php把session写入数据库示例
2014/02/26 PHP
PHP中多维数组的foreach遍历示例
2014/06/13 PHP
PHP date函数常用时间处理方法
2015/05/11 PHP
微信公众平台开发之天气预报功能
2015/08/31 PHP
php-fpm重启导致的程序执行中断问题详解
2019/04/29 PHP
基于jquery实现控制经纬度显示地图与卫星
2013/05/20 Javascript
复制js对象方法(详解)
2013/07/08 Javascript
使用jquery prev()方法找到同级的前一个元素
2014/07/11 Javascript
9款2014最热门jQuery实用特效推荐
2014/12/07 Javascript
jQuery中DOM树操作之使用反向插入方法实例分析
2015/01/23 Javascript
基于JQuery实现仿网易邮箱全屏动感滚动插件fullPage
2015/09/20 Javascript
Bootstrap模态对话框的简单使用
2016/04/29 Javascript
js统计页面上每个标签的数量实例代码
2018/05/29 Javascript
js的继承方法小结(prototype、call、apply)(推荐)
2019/04/17 Javascript
js屏蔽F12审查元素,禁止修改页面代码等实现代码
2020/10/02 Javascript
微信小程序tab左右滑动切换功能的实现代码
2021/02/08 Javascript
[46:43]DOTA2上海特级锦标赛D组小组赛#1 EG VS COL第三局
2016/02/28 DOTA
[07:09]DOTA2-DPC中国联赛 正赛 Ehome vs Elephant 选手采访
2021/03/11 DOTA
Python学习笔记(一)(基础入门之环境搭建)
2014/06/05 Python
实例介绍Python中整型
2019/02/11 Python
python实现批量修改文件名
2020/03/23 Python
Volcom法国官网:美国冲浪滑板品牌
2017/05/25 全球购物
HomeAway的巴西品牌:Alugue Temporada
2018/04/10 全球购物
Champion澳大利亚官网:美国冠军运动服装
2018/05/07 全球购物
Ancheer官方户外和运动商店:销售电动自行车
2019/08/07 全球购物
水利学院求职自荐书
2014/02/01 职场文书
保险内勤岗位职责
2014/04/05 职场文书
个人工作表现评语
2014/04/30 职场文书
Python OpenCV实现传统图片格式与base64转换
2021/06/13 Python
Redis做数据持久化的解决方案及底层原理
2021/07/15 Redis
python编程实现清理微信重复缓存文件
2021/11/01 Python
MySQL数据库如何使用Shell进行连接
2022/04/12 MySQL
pt-archiver 主键自增
2022/04/26 MySQL
MySQL中正则表达式(REGEXP)使用详解
2022/07/07 MySQL
clear 万能清除浮动(clearfix:after)
2023/05/21 HTML / CSS