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 相关文章推荐
js querySelector和getElementById通过id获取元素的区别
Apr 20 Javascript
jQuery实用技巧必备(上)
Nov 02 Javascript
基于JS实现新闻列表无缝向上滚动实例代码
Jan 22 Javascript
基于BootStrap Metronic开发框架经验小结【四】Bootstrap图标的提取和利用
May 12 Javascript
jQuery1.9+中删除了live以后的替代方法
Jun 17 Javascript
js中动态创建json,动态为json添加属性、属性值的实例
Dec 02 Javascript
在Vue组件中使用 TypeScript的方法
Feb 28 Javascript
详解javascript中的babel到底是什么
Jun 21 Javascript
浅谈layui里的上传控件问题
Sep 26 Javascript
微信小程序 动态修改页面数据及参数传递过程详解
Sep 27 Javascript
layui数据表格重载实现往后台传参
Nov 15 Javascript
antd多选下拉框一行展示的实现方式
Oct 31 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函数
2006/10/09 PHP
一步一步学习PHP(8) php 数组
2010/03/05 PHP
php 删除记录同时删除图片文件的实现代码
2010/05/12 PHP
PHP 强制性文件下载功能的函数代码(任意文件格式)
2010/05/26 PHP
PHP采用自定义函数实现遍历目录下所有文件的方法
2014/08/19 PHP
javascript some()函数用法详解
2014/11/13 PHP
php中$_POST与php://input的区别实例分析
2015/01/07 PHP
Zend Framework教程之Zend_Form组件实现表单提交并显示错误提示的方法
2016/03/21 PHP
asp javascript 实现关闭窗口时保存数据的办法
2007/11/24 Javascript
jQuery.autocomplete 支持中文输入(firefox)修正方法
2011/03/10 Javascript
jquery解决图片路径不存在执行替换路径
2013/02/06 Javascript
js利用prototype调用Array的slice方法示例
2014/06/09 Javascript
js实现滑动触屏事件监听的方法
2015/05/05 Javascript
分享经典的JavaScript开发技巧
2015/11/21 Javascript
全面解析Bootstrap排版使用方法(标题)
2015/11/30 Javascript
jQuery Easyui使用(一)之可折叠面板的布局手风琴菜单
2016/08/17 Javascript
JavaScript中使用参数个数实现重载功能
2017/09/01 Javascript
JavaScript中常用的简洁高级技巧总结
2019/03/10 Javascript
Python中让MySQL查询结果返回字典类型的方法
2014/08/22 Python
用Python抢过年的火车票附源码
2015/12/07 Python
Pycharm编辑器技巧之自动导入模块详解
2017/07/18 Python
浅谈Pandas中map, applymap and apply的区别
2018/04/10 Python
Python装饰器模式定义与用法分析
2018/08/06 Python
python super用法及原理详解
2020/01/20 Python
解决pyqt5异常退出无提示信息的问题
2020/04/08 Python
用 python 进行微信好友信息分析
2020/11/28 Python
突袭HTML5之Javascript API扩展5—其他扩展(应用缓存/服务端消息/桌面通知)
2013/01/31 HTML / CSS
HTML5中的autofocus(自动聚焦)属性介绍
2014/04/23 HTML / CSS
美国最大和最受信任的二手轮胎商店:Bestusedtires.com
2020/06/02 全球购物
运动会稿件50字
2014/02/17 职场文书
绩效管理实施方案
2014/03/19 职场文书
个人欠款担保书
2014/05/20 职场文书
小学六一儿童节活动方案
2014/08/27 职场文书
幼儿园教师考核评语
2014/12/31 职场文书
无锡灵山大佛导游词
2015/02/09 职场文书
《圆的周长》教学反思
2016/02/17 职场文书