mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-17 07:40:22 +00:00
147 lines
6.5 KiB
Text
Executable file
147 lines
6.5 KiB
Text
Executable file
@workInProgress
|
|
@ngdoc overview
|
|
@name Tutorial: Step 5
|
|
@description
|
|
<table id="tutorial_nav">
|
|
<tr>
|
|
<td id="previous_step">{@link tutorial.step_00 Previous}</td>
|
|
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-5/app Example}</td>
|
|
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
|
<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-4...step-5 Code
|
|
Diff}</td>
|
|
<td id="next_step">{@link tutorial.step_00 Next}</td>
|
|
</tr>
|
|
</table>
|
|
|
|
In this step, the View template remains the same but the Model and Controller change. We'll
|
|
introduce the use of an angular {@link angular.service service}, which we will use to implement an
|
|
`XMLHttpRequest` request to communicate with a server. Angular provides the built-in {@link
|
|
angular.service.$xhr $xhr} service to make this easy.
|
|
|
|
The addition of the `$xhr` service to our app gives us the opportunity to talk about {@link
|
|
guide.di Dependency Injection} (DI). The use of DI is another cornerstone of the angular
|
|
philosophy. DI helps make your web apps well structured, loosely coupled, and ultimately easier to
|
|
test.
|
|
|
|
__`app/js/controllers.js:`__
|
|
<pre>
|
|
/* App Controllers */
|
|
|
|
function PhoneListCtrl($xhr) {
|
|
var self = this;
|
|
|
|
$xhr('GET', 'phones/phones.json', function(code, response) {
|
|
self.phones = response;
|
|
});
|
|
|
|
self.orderProp = 'age';
|
|
}
|
|
|
|
//PhoneListCtrl.$inject = ['$xhr'];
|
|
</pre>
|
|
|
|
__`test/unit/controllerSpec.js`:__
|
|
<pre>
|
|
/* jasmine specs for controllers go here */
|
|
describe('PhoneCat controllers', function() {
|
|
|
|
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).toBeUndefined();
|
|
$browser.xhr.flush();
|
|
|
|
expect(ctrl.phones).toEqual([{name: 'Nexus S'},
|
|
{name: 'Motorola DROID'}]);
|
|
});
|
|
|
|
|
|
it('should set the default value of orderProp model', function() {
|
|
expect(ctrl.orderProp).toBe('age');
|
|
});
|
|
});
|
|
});
|
|
</pre>
|
|
|
|
## Discussion:
|
|
|
|
* __Services:__ {@link angular.service Services} are substitutable objects managed by angular's
|
|
{@link guide.di DI subsystem}. Angular services simplify some of the standard operations common
|
|
to web apps. Angular provides several built-in services (such as {@link angular.service.$xhr
|
|
$xhr}). You can also create your own custom services.
|
|
|
|
* __Dependency Injection:__ To use an angular service, you simply provide the name of the service
|
|
as an argument to the controller's constructor function. The name of the argument is significant,
|
|
because angular's {@link guide.di DI subsystem} recognizes the identity of a service by its name,
|
|
and provides the name of the service to the controller during the controller's construction. The
|
|
dependency injector also takes care of creating any transitive dependencies the service may have
|
|
(services often depend upon other services).
|
|
|
|
Note: if you minify the javascript code for this controller, all function arguments will be
|
|
minified as well. This will result in the dependency injector not being able to identify
|
|
services correctly. To overcome this issue, just assign an array with service identifier strings
|
|
into the `$inject` property of the controller function.
|
|
|
|
* __`$xhr`:__ We moved our data set out of the controller and into the file
|
|
`app/phones/phones.json` (and added some more phones). We used the `$xhr` service to make a GET
|
|
HTTP request to our web server, asking for `phone/phones.json` (the url is relative to our
|
|
`index.html` file). The server responds with the contents of the json file, which serves as the
|
|
source of our data. Keep in mind that the response might just as well have been dynamically
|
|
generated by a sophisticated backend server. To our web server they both look the same, but using
|
|
a real backend server to generate a response would make our tutorial unnecessarily complicated.
|
|
|
|
Notice that the $xhr service takes a callback as the last parameter. This callback is used to
|
|
process the response. In our case, we just assign the response to the current scope controlled
|
|
by the controller, as a model called `phones`. Have you realized that we didn't even have to
|
|
parse the response? Angular took care of that for us.
|
|
|
|
* __Testing:__ The unit tests have been expanded. Because of the dependency injection business,
|
|
we now need to create the controller the same way that angular does it behind the scenes. For this
|
|
reason, we need to:
|
|
|
|
* Create a root scope object by calling `angular.scope()`
|
|
|
|
* Call `scope.$new(PhoneListCtrl)` to get angular to create the child scope associated with
|
|
our controller.
|
|
|
|
At the same time, we need to tell the testing harness that it should expect an incoming
|
|
request from our controller. To do this we:
|
|
|
|
* Use the `$service` method to retrieve the `$browser` service - this is a service that in
|
|
angular represents various browser APIs. In tests, angular automatically uses a mock version
|
|
of this service that allows you to write tests without having to deal with these native APIs
|
|
and the global state associated with them.
|
|
|
|
* We use the `$browser.expectGET` method to train the `$browser` object to expect an incoming
|
|
http request and tell it what to respond with. Note that the responses are not returned before
|
|
we call the `$browser.xhr.flush()` method.
|
|
|
|
* We then make assertions to verify that the `phones` model doesn't exist on the scope, before
|
|
the response is received.
|
|
|
|
* We flush the xhr queue in the browser by calling `$browser.xhr.flush()`. This causes the
|
|
callback we passed into the `$xhr` service to be executed with the trained response.
|
|
|
|
* Finally, we make the assertions, verifying that the phone model now exists on the scope.
|
|
|
|
<table id="tutorial_nav">
|
|
<tr>
|
|
<td id="previous_step">{@link tutorial.step_00 Previous}</td>
|
|
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-5/app Example}</td>
|
|
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
|
<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-4...step-5
|
|
Code Diff}</td>
|
|
<td id="next_step">{@link tutorial.step_00 Next}</td>
|
|
</tr>
|
|
</table>
|