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 相关文章推荐
关于Jqzoom的使用心得 jquery放大镜效果插件
Apr 12 Javascript
jquery+json 通用三级联动下拉列表
Apr 19 Javascript
一些javascript一些题目的解析
Dec 25 Javascript
从零开始学习jQuery (四) jQuery中操作元素的属性与样式
Feb 23 Javascript
jquery 获取dom固定元素 添加样式的简单实例
Feb 04 Javascript
JavaScript调试技巧之console.log()详解
Mar 19 Javascript
AngularJS iframe跨域打开内容时报错误的解决办法
Jan 26 Javascript
Bootstrap每天必学之前端开发框架
Nov 19 Javascript
基于jQuery实现音乐播放试听列表
Apr 14 Javascript
jQuery控制元素隐藏和显示
Mar 03 Javascript
jquery实现Ajax请求的几种常见方式总结
May 28 jQuery
JavaScript 替换所有匹配内容及正则替换方法
Feb 12 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 数字左侧自动补0
2008/03/31 PHP
php 在线打包_支持子目录
2008/06/28 PHP
PHP图片处理之图片旋转和图片翻转实例
2014/11/19 PHP
经典PHP加密解密函数Authcode()修复版代码
2015/04/05 PHP
JScript中的&quot;this&quot;关键字使用方式补充材料
2007/03/08 Javascript
JavaScript 变量基础知识
2009/11/07 Javascript
解决3.01版的jquery.form.js中文乱码问题的解决方法
2012/03/08 Javascript
JQuery切换显示的效果实例代码
2013/02/27 Javascript
jquery实现盒子下拉效果示例代码
2013/09/12 Javascript
javascript数组输出的两种方式
2015/01/13 Javascript
nodejs中实现sleep功能实例
2015/03/24 NodeJs
js实现的全国省市二级联动下拉选择菜单完整实例
2015/08/17 Javascript
jQuery实现下滑菜单导航效果代码
2015/08/25 Javascript
关于JS中prototype的理解
2015/09/07 Javascript
js接收并转化Java中的数组对象的方法
2016/08/11 Javascript
JS实现针对给定时间的倒计时功能示例
2017/04/11 Javascript
巧用weui.topTips验证数据的实例
2017/04/17 Javascript
jQuery插件imgAreaSelect基础讲解
2017/05/26 jQuery
angularJS利用ng-repeat遍历二维数组的实例代码
2017/06/03 Javascript
swiper自定义分页器使用方法详解
2020/09/14 Javascript
关于vue3默认把所有onSomething当作v-on事件绑定的思考
2020/05/15 Javascript
python3实现ftp服务功能(客户端)
2017/03/24 Python
python中的turtle库函数简单使用教程
2018/07/23 Python
opencv转换颜色空间更改图片背景
2019/08/20 Python
解决python多行注释引发缩进错误的问题
2019/08/23 Python
TensorFlow MNIST手写数据集的实现方法
2020/02/05 Python
加拿大在线眼镜零售商:SmartBuyGlasses加拿大
2019/05/25 全球购物
仓管员岗位责任制
2014/02/19 职场文书
《春笋》教学反思
2014/04/15 职场文书
技术合作协议书范本
2014/04/18 职场文书
任命书怎么写
2014/06/04 职场文书
社区母亲节活动总结
2015/02/10 职场文书
2015学习委员工作总结范文
2015/04/03 职场文书
如何使用flask将模型部署为服务
2021/05/13 Python
【海涛教你打DOTA】死灵飞龙第一视角解说
2022/04/01 DOTA
Android实现图片九宫格
2022/06/28 Java/Android