aboutsummaryrefslogtreecommitdiffstats
path: root/docs/tutorial.step_11.ngdoc
blob: 34ba7b022e5d2011d6282f44f4fd86b08fd2d26d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
@workInProgress
@ngdoc overview
@name Tutorial: Step 11
@description
<table id="tutorial_nav">
<tr>
<td id="previous_step">{@link tutorial.step_10 Previous}</td>
<td id="step_result">{@link  http://angular.github.com/angular-phonecat/step-11/app Live Demo
}</td>
<td id="tut_home">{@link tutorial Tutorial Home}</td>
<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-10...step-11
Code Diff}</td>
<td id="next_step">Next</td>
</tr>
</table>

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`.__ 
<pre>
...
  <script src="js/services.js"></script>
...
</pre>


__`app/js/services.js`.__ (New)
<pre>
 angular.service('Phone', function($resource){
  return $resource('phones/:phoneId.json', {}, {
    query: {method:'GET', params:{phoneId:'phones'}, isArray:true}
  });
 });
</pre>

__`app/js/controllers.js`.__
<pre>
...

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'];
</pre>

__`test/unit/controllersSpec.js`:__
<pre>
/* 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'});
    });
  });
});
</pre>


## 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.

<table id="tutorial_nav">
<tr>
<td id="previous_step">{@link tutorial.step_10 Previous}</td>
<td id="step_result">{@link  http://angular.github.com/angular-phonecat/step-11/app Live Demo
}</td>
<td id="tut_home">{@link tutorial Tutorial Home}</td>
<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-10...step-11
Code Diff}</td>
<td id="next_step">Next</td>
</tr>
</table>