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实现的分页函数
Feb 07 Javascript
javascript 动态调整图片尺寸实现代码
Dec 28 Javascript
javascript实现下拉提示选择框
Dec 29 Javascript
用js写的一个路由(简单实例)
Sep 24 Javascript
jQuery UI插件实现百度提词器效果
Nov 21 Javascript
jQuery.Form实现Ajax上传文件同时设置headers的方法
Jun 26 jQuery
jQuery Pagination分页插件_动力节点Java学院整理
Jul 17 jQuery
基于vue2实现左滑删除功能
Nov 28 Javascript
微信小程序实现刷脸登录
May 25 Javascript
layui给下拉框、按钮状态、时间赋初始值的方法
Sep 10 Javascript
vue点击自增和求和的实例代码
Nov 06 Javascript
JavaScript数组排序功能简单实现
May 14 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-accelerator网站加速PHP缓冲的方法
2008/07/30 PHP
PHP常用的文件操作函数经典收藏
2013/04/02 PHP
PHP利用二叉堆实现TopK-算法的方法详解
2017/04/24 PHP
Knockoutjs快速入门(经典)
2012/12/24 Javascript
jquery next nextAll nextUntil siblings的区别介绍
2013/10/05 Javascript
Jquery使用Firefox FireBug插件调试Ajax步骤讲解
2013/12/02 Javascript
javascript模拟C#格式化字符串
2015/08/26 Javascript
详解JavaScript编程中正则表达式的使用
2015/10/25 Javascript
JS中创建函数的三种方式及区别
2016/03/13 Javascript
JS控制FileUpload的上传文件类型实例代码
2016/10/07 Javascript
jQuery实现页面滚动时智能浮动定位
2017/01/08 Javascript
JavaScript切换搜索引擎的导航网页搜索框实例代码
2017/06/11 Javascript
详解JavaScript中的六种错误类型
2017/09/21 Javascript
原生JS获取元素的位置与尺寸实现方法
2017/10/18 Javascript
Vue编写可显示周和月模式的日历 Vue自定义日历内容的显示
2019/06/26 Javascript
jQuery zTree插件使用简单教程
2019/08/16 jQuery
Vue项目中使用jsonp抓取跨域数据的方法
2019/11/10 Javascript
Vue+Node服务器查询Mongo数据库及页面数据传递操作实例分析
2019/12/20 Javascript
详解基于element的区间选择组件校验(交易金额)
2021/01/07 Javascript
简单介绍利用TK在Python下进行GUI编程的教程
2015/04/13 Python
Windows下PyMongo下载及安装教程
2015/04/27 Python
Django Admin实现三级联动的示例代码(省市区)
2018/06/22 Python
浅述python2与python3的简单区别
2018/09/19 Python
一篇文章搞懂Python的类与对象名称空间
2018/12/10 Python
python-numpy-指数分布实例详解
2019/12/07 Python
基于plt.title无法显示中文的快速解决
2020/05/16 Python
MYSQL相比于其他数据库有哪些特点
2013/07/19 面试题
医药大学生求职简历的自我评价
2013/10/17 职场文书
护士思想汇报
2014/01/12 职场文书
详细的大学生创业计划书模板
2014/01/27 职场文书
棉花姑娘教学反思
2014/02/15 职场文书
乡镇民主生活会发言材料
2014/10/20 职场文书
2015年推普周活动方案
2015/05/06 职场文书
奠基仪式致辞
2015/07/30 职场文书
基于Python实现对比Exce的工具
2022/04/07 Python
el-form每行显示两列底部按钮居中效果的实现
2022/08/05 HTML / CSS