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 相关文章推荐
JavaScript 原型链学习总结
Oct 29 Javascript
超炫的jquery仿flash导航栏特效
Nov 11 Javascript
jQuery实现按键盘方向键翻页特效
Mar 18 Javascript
简介JavaScript中的push()方法的使用
Jun 09 Javascript
jQuery实现仿腾讯视频列表分页效果的方法
Aug 07 Javascript
基于JS实现EOS隐藏错误提示层代码
Apr 25 Javascript
Bootstrap table两种分页示例
Dec 23 Javascript
Angular.js中控制器之间的传值详解
Apr 24 Javascript
vue之nextTick全面解析
May 17 Javascript
利用JS对iframe父子(内外)页面进行操作的方法教程
Jun 15 Javascript
Vue匿名插槽与作用域插槽的合并和覆盖行为
Apr 22 Javascript
vue视频播放暂停代码
Nov 08 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报表之jpgraph柱状图实例代码
2011/08/22 PHP
PHP设计模式之抽象工厂模式实例分析
2019/03/25 PHP
基于jquery的checkbox下拉框插件代码
2010/06/25 Javascript
js 模拟气泡屏保效果代码
2010/07/10 Javascript
读jQuery之一(对象的组成)
2011/06/11 Javascript
通过JS自动隐藏手机浏览器的地址栏实现原理与代码
2013/01/02 Javascript
jQuery中hasClass()方法用法实例
2015/01/06 Javascript
AngularJS创建自定义指令的方法详解
2016/11/03 Javascript
详解微信小程序 wx.uploadFile 的编码坑
2017/01/23 Javascript
详解vue-cli + webpack 多页面实例配置优化方法
2017/07/13 Javascript
js实现简易聊天对话框
2017/08/17 Javascript
vue项目中使用axios上传图片等文件操作
2017/11/02 Javascript
bootstrap 通过加减按钮实现输入框组功能
2017/11/15 Javascript
Vue项目服务器部署之子目录部署方法
2019/05/12 Javascript
layui异步加载table表中某一列数据的例子
2019/09/16 Javascript
javaScript中indexOf用法技巧
2019/11/26 Javascript
微信小程序上传帖子的实例代码(含有文字图片的微信验证)
2020/07/11 Javascript
vue项目实现设置根据路由高亮对应的菜单项操作
2020/08/06 Javascript
浅谈鸿蒙 JavaScript GUI 技术栈
2020/09/17 Javascript
Python实现提取文章摘要的方法
2015/04/21 Python
python matplotlib坐标轴设置的方法
2017/12/05 Python
Python操作MySQL数据库的三种方法总结
2018/01/30 Python
解决PyCharm控制台输出乱码的问题
2019/01/16 Python
python中append实例用法总结
2019/07/30 Python
python打造爬虫代理池过程解析
2019/08/15 Python
Python如何实现大型数组运算(使用NumPy)
2020/07/24 Python
python 用opencv实现霍夫线变换
2020/11/27 Python
浅析移动设备HTML5页面布局
2015/12/01 HTML / CSS
北京天润融通.net面试题笔试题
2012/02/20 面试题
如何清空Session
2015/02/23 面试题
服装采购员岗位职责
2014/03/15 职场文书
《小动物过冬》教学反思
2014/04/17 职场文书
学生检讨书如何写
2014/10/30 职场文书
计算机实训报告总结
2014/11/05 职场文书
大学生创业计划书
2019/06/24 职场文书
python利用pandas分析学生期末成绩实例代码
2021/07/09 Python