aboutsummaryrefslogtreecommitdiffstats
path: root/docs/content/tutorial/step_05.ngdoc
diff options
context:
space:
mode:
authorIgor Minar2012-03-30 14:02:26 -0700
committerIgor Minar2012-04-04 15:59:18 -0700
commit6336b6e89e3a80aec3c4367ab4c2737fd365c030 (patch)
tree31aa86a0555b541d1f6cc107845278ae80ddbff9 /docs/content/tutorial/step_05.ngdoc
parentfdf17d729fa7651e88dc5f27c40b8de875a34a55 (diff)
downloadangular.js-6336b6e89e3a80aec3c4367ab4c2737fd365c030.tar.bz2
chore(docs): restore old tutorial ngdoc files
Diffstat (limited to 'docs/content/tutorial/step_05.ngdoc')
-rw-r--r--docs/content/tutorial/step_05.ngdoc216
1 files changed, 216 insertions, 0 deletions
diff --git a/docs/content/tutorial/step_05.ngdoc b/docs/content/tutorial/step_05.ngdoc
new file mode 100644
index 00000000..7bf6f708
--- /dev/null
+++ b/docs/content/tutorial/step_05.ngdoc
@@ -0,0 +1,216 @@
+@ngdoc overview
+@name Tutorial: 5 - XHRs & Dependency Injection
+@description
+
+<ul doc:tutorial-nav="5"></ul>
+
+
+Enough of building an app with three phones in a hard-coded dataset! Let's fetch a larger dataset
+from our server using one of angular's built-in {@link api/angular.module.ng services} called {@link
+api/angular.module.ng.$xhr $xhr}. We will use angular's {@link guide/dev_guide.di dependency
+injection (DI)} to provide the service to the `PhoneListCtrl` controller.
+
+
+<doc:tutorial-instructions step="5"></doc:tutorial-instructions>
+
+
+You should now see a list of 20 phones.
+
+The most important changes are listed below. You can see the full diff on {@link
+https://github.com/angular/angular-phonecat/compare/step-4...step-5
+GitHub}:
+
+## Data
+
+The `app/phones/phone.json` file in your project is a dataset that contains a larger list of phones
+stored in the JSON format.
+
+Following is a sample of the file:
+<pre>
+[
+ {
+ "age": 13,
+ "id": "motorola-defy-with-motoblur",
+ "name": "Motorola DEFY\u2122 with MOTOBLUR\u2122",
+ "snippet": "Are you ready for everything life throws your way?"
+ ...
+ },
+...
+]
+</pre>
+
+
+## Controller
+
+We'll use angular's {@link api/angular.module.ng.$xhr $xhr} service in our controller to make an HTTP
+request to your web server to fetch the data in the `app/phones/phones.json` file. `$xhr` is just
+one of several built-in {@link api/angular.module.ng angular services} that handle common operations
+in web apps. Angular injects these services for you where you need them.
+
+Services are managed by angular's {@link guide/dev_guide.di DI subsystem}. Dependency injection
+helps to make your web apps both well-structured (e.g., separate components for presentation, data,
+and control) and loosely coupled (dependencies between components are not resolved by the
+components themselves, but by the DI subsystem).
+
+__`app/js/controllers.js:`__
+<pre>
+function PhoneListCtrl($xhr) {
+ var self = this;
+
+ $xhr('GET', 'phones/phones.json', function(code, response) {
+ self.phones = response;
+ });
+
+ self.orderProp = 'age';
+}
+
+//PhoneListCtrl.$inject = ['$xhr'];
+</pre>
+
+`$xhr` makes an HTTP GET request to our web server, asking for `phone/phones.json` (the url is
+relative to our `index.html` file). The server responds by providing the data in the json file.
+(The response might just as well have been dynamically generated by a backend server. To the
+browser and our app they both look the same. For the sake of simplicity we used a json file in this
+tutorial.)
+
+The `$xhr` service takes a callback as the last argument. This callback is used to process the
+response. We assign the response to the scope controlled by the controller, as a model called
+`phones`. Notice that angular detected the json response and parsed it for us!
+
+To use a service in angular, you simply declare the names of the services you need as arguments to
+the controller's constructor function, as follows:
+
+ function PhoneListCtrl($xhr) {...}
+
+Angular's dependency injector provides services to your controller when the controller is being
+constructed. The dependency injector also takes care of creating any transitive dependencies the
+service may have (services often depend upon other services).
+
+<img src="img/tutorial/xhr_service_final.png">
+
+
+### '$' Prefix Naming Convention
+
+You can create your own services, and in fact we will do exactly that in step 11. As a naming
+convention, angular's built-in services, Scope methods and a few other angular APIs have a '$'
+prefix in front of the name. Don't use a '$' prefix when naming your services and models, in order
+to avoid any possible naming collisions.
+
+### A Note on Minification
+
+Since angular infers the controller's dependencies from the names of arguments to the controller's
+constructor function, if you were to {@link http://en.wikipedia.org/wiki/Minification_(programming)
+minify} the JavaScript code for `PhoneListCtrl` controller, all of its function arguments would be
+minified as well, and the dependency injector would not being able to identify services correctly.
+
+To overcome issues caused by minification, just assign an array with service identifier strings
+into the `$inject` property of the controller function, just like the last line in the snippet
+(commented out) suggests:
+
+ PhoneListCtrl.$inject = ['$xhr'];
+
+
+## Test
+
+__`test/unit/controllersSpec.js`:__
+
+Because we started using dependency injection and our controller has dependencies, constructing the
+controller in our tests is a bit more complicated. We could use the `new` operator and provide the
+constructor with some kind of fake `$xhr` implementation. However, the recommended (and easier) way
+is to create a controller in the test environment in the same way that angular does it in the
+production code behind the scenes, as follows:
+
+<pre>
+describe('PhoneCat controllers', function() {
+
+ describe('PhoneListCtrl', function() {
+ var scope, $browser, ctrl;
+
+ beforeEach(function() {
+ scope = angular.module.ng.$rootScope.Scope();
+ $browser = scope.$service('$browser');
+
+ $browser.xhr.expectGET('phones/phones.json')
+ .respond([{name: 'Nexus S'},
+ {name: 'Motorola DROID'}]);
+ ctrl = scope.$new(PhoneListCtrl);
+ });
+ });
+</pre>
+
+We created the controller in the test environment, as follows:
+
+* We created a root scope object by calling `angular.module.ng.$rootScope.Scope()`
+
+* We called `scope.$new(PhoneListCtrl)` to get angular to create the child scope associated with
+the `PhoneListCtrl` controller
+
+Because our code now uses the `$xhr` service to fetch the phone list data in our controller, before
+we create the `PhoneListCtrl` child scope, we need to tell the testing harness to expect an
+incoming request from the controller. To do this we:
+
+* Use the {@link api/angular.module.ng.$rootScope.Scope#$service `$service`} method to retrieve the `$browser` service,
+a service that angular uses to represent 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.
+
+* Use the `$browser.xhr.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.
+
+Now, we will make assertions to verify that the `phones` model doesn't exist on the scope, before
+the response is received:
+
+<pre>
+ 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'}]);
+ });
+</pre>
+
+* 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.
+
+* We make the assertions, verifying that the phone model now exists on the scope.
+
+Finally, we verify that the default value of `orderProp` is set correctly:
+
+<pre>
+ it('should set the default value of orderProp model', function() {
+ expect(ctrl.orderProp).toBe('age');
+ });
+ });
+});
+</pre>
+
+To run the unit tests, execute the `./scripts/test.sh` script and you should see the following
+output.
+
+ Chrome: Runner reset.
+ ..
+ Total 2 tests (Passed: 2; Fails: 0; Errors: 0) (3.00 ms)
+ Chrome 11.0.696.57 Mac OS: Run 2 tests (Passed: 2; Fails: 0; Errors 0) (3.00 ms)
+
+
+# Experiments
+
+* At the bottom of `index.html`, add a `{{phones}}` binding to see the list of phones displayed in
+json format.
+
+* In the `PhoneListCtrl` controller, pre-process the xhr response by limiting the number of phones
+to the first 5 in the list. Use the following code in the xhr callback:
+
+ self.phones = response.splice(0, 5);
+
+
+# Summary
+
+Now that you have learned how easy it is to use angular services (thanks to angular's
+implementation of dependency injection), go to {@link step_06 step 6}, where you will add some
+thumbnail images of phones and some links.
+
+
+<ul doc:tutorial-nav="5"></ul>