@workInProgress @ngdoc overview @name Tutorial: Step 11 @description
{@link tutorial.step_10 Previous} {@link http://angular.github.com/angular-phonecat/step-11/app Live Demo } {@link tutorial Tutorial Home} {@link https://github.com/angular/angular-phonecat/compare/step-10...step-11 Code Diff} Next
And so we arrive at the last step of this tutorial. Here we define a custom service that represents a {@link http://en.wikipedia.org/wiki/Representational_State_Transfer RESTful} client. Using this client we can make xhr requests for data in an easier way, without having to deal with the lower-level {@link angular.service.$xhr $xhr} APIs, HTTP methods and URLs. __`app/index.html`.__
...
  
...
__`app/js/services.js`.__ (New)
 angular.service('Phone', function($resource){
  return $resource('phones/:phoneId.json', {}, {
    query: {method:'GET', params:{phoneId:'phones'}, isArray:true}
  });
 });
__`app/js/controllers.js`.__
...

function PhoneListCtrl(Phone_) {
  this.orderProp = 'age';
  this.phones = Phone_.query();
}
//PhoneListCtrl.$inject = ['Phone'];


function PhoneDetailCtrl(Phone_) {
  this.phone = Phone_.get({phoneId:this.params.phoneId});
}
//PhoneDetailCtrl.$inject = ['Phone'];
__`test/unit/controllersSpec.js`:__
/* jasmine specs for controllers go here */
describe('PhoneCat controllers', function() {

  beforeEach(function(){
    this.addMatchers({
      toEqualData: function(expected) {
        return angular.equals(this.actual, expected);
      }
    });
  });

  describe('PhoneListCtrl', function(){
    var scope, $browser, ctrl;

    beforeEach(function() {
      scope = angular.scope();
      $browser = scope.$service('$browser');

      $browser.xhr.expectGET('phones/phones.json').respond([{name: 'Nexus S'},
                                                            {name: 'Motorola DROID'}]);
      ctrl = scope.$new(PhoneListCtrl);
    });

    it('should create "phones" model with 2 phones fetched from xhr', function() {
      expect(ctrl.phones).toEqual([]);
      $browser.xhr.flush();

      expect(ctrl.phones).toEqualData([{name: 'Nexus S'},
                                       {name: 'Motorola DROID'}]);
    });

    it('should set the default value of orderProp model', function() {
      expect(ctrl.orderProp).toBe('age');
    });
  });


  describe('PhoneDetailCtrl', function(){
    var scope, $browser, ctrl;

    beforeEach(function() {
      scope = angular.scope();
      $browser = scope.$service('$browser');
    });

    beforeEach(function() {
      scope = angular.scope();
      $browser = scope.$service('$browser');
    });

    it('should fetch phone detail', function(){
      scope.params = {phoneId:'xyz'};
      $browser.xhr.expectGET('phones/xyz.json').respond({name:'phone xyz'});
      ctrl = scope.$new(PhoneDetailCtrl);

      expect(ctrl.phone).toEqualData({});
      $browser.xhr.flush();

      expect(ctrl.phone).toEqualData({name:'phone xyz'});
    });
  });
});
## Discussion: * We simplified our sub-controllers (`PhoneListCtrl` and `PhoneDetailCtrl`) by factoring out the lower-level `$xhr` service, replacing it with a new service called `Phone`. Angular's {@link angular.service.$resource `$resource`} service is easier to use than `$xhr` for interacting with data sources exposed as RESTful resources. It is also easier now to understand what the code in our controllers is doing. An important thing to notice in our controller code is that we don't pass any callback functions when invoking methods of our Phone services. It looks as if the result were returned synchronously. That is not the case at all. What is returned synchronously is a "future" — an object, which will be filled with data when the xhr response returns. Because of the data-binding in angular, we can use this future and bind it to our template. Then, when the data arrives, the view will automatically update. See? Angular tries hard to make simple stuff simple. * Once again we make use of `$route's` params, this time to construct the URL passed as a parameter to `$resource` in our `services.js` script. * Last, but certainly not least, we expanded and modified our unit test to verify that our new service is returning data as we expect it to. In our assertions we use a newly-defined `toEqualData` {@link http://pivotal.github.com/jasmine/jsdoc/symbols/jasmine.Matchers.html Jasmine matcher}, which compares only object properties and ignores methods. This is necessary, because the `$resource` client will augment the response object with handy methods for updating and deleting the resource (we don't use these in our tutorial though). There you have it! We have created a web app in a relatively short amount of time. ## Closing Notes: * For more details and examples of the angular concepts we touched on in this tutorial, see the {@link guide Developer Guide}. * For several more examples of sample code, see the {@link cookbook Cookbook}. * When you are ready to start developing a project using angular, be sure to begin with the {@link https://github.com/angular/angular-seed angular seed app}. * We hope this tutorial was useful to you, and that you learned enough about angular to make you want to learn more. Of course, we especially hope you are inspired to go out and develop angular web apps of your own, and perhaps you might even be interested in {@link contribute contributing} to angular.
{@link tutorial.step_10 Previous} {@link http://angular.github.com/angular-phonecat/step-11/app Live Demo } {@link tutorial Tutorial Home} {@link https://github.com/angular/angular-phonecat/compare/step-10...step-11 Code Diff} Next