Update everything
This commit is contained in:
parent
bf368181a4
commit
72a485d6e8
319 changed files with 67958 additions and 13948 deletions
227
lib/angular/docs/partials/tutorial/step_05.html
Normal file
227
lib/angular/docs/partials/tutorial/step_05.html
Normal file
|
@ -0,0 +1,227 @@
|
|||
<h1><code ng:non-bindable=""></code>
|
||||
<span class="hint"></span>
|
||||
</h1>
|
||||
<div><ul doc-tutorial-nav="5"></ul>
|
||||
|
||||
<p>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 <a href="api/ng">services</a> called <a href="api/ng.$http"><code>$http</code></a>. We will use angular's <a href="guide/di">dependency injection (DI)</a> to provide the service to the <code>PhoneListCtrl</code> controller.</p>
|
||||
|
||||
<div doc-tutorial-reset="5">
|
||||
</div>
|
||||
|
||||
<p>You should now see a list of 20 phones.</p>
|
||||
|
||||
<p>The most important changes are listed below. You can see the full diff on <a href="https://github.com/angular/angular-phonecat/compare/step-4...step-5">GitHub</a>:</p>
|
||||
|
||||
<h3>Data</h3>
|
||||
|
||||
<p>The <code>app/phones/phones.json</code> file in your project is a dataset that contains a larger list of phones
|
||||
stored in the JSON format.</p>
|
||||
|
||||
<p>Following is a sample of the file:
|
||||
<pre class="prettyprint linenums">
|
||||
[
|
||||
{
|
||||
"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>
|
||||
|
||||
<h3>Controller</h3>
|
||||
|
||||
<p>We'll use angular's <a href="api/ng.$http"><code>$http</code></a> service in our controller to make an HTTP
|
||||
request to your web server to fetch the data in the <code>app/phones/phones.json</code> file. <code>$http</code> is just
|
||||
one of several built-in <a href="api/ng">angular services</a> that handle common operations
|
||||
in web apps. Angular injects these services for you where you need them.</p>
|
||||
|
||||
<p>Services are managed by angular's <a href="guide/di">DI subsystem</a>. 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).</p>
|
||||
|
||||
<p><strong><code>app/js/controllers.js:</code></strong>
|
||||
<pre class="prettyprint linenums">
|
||||
function PhoneListCtrl($scope, $http) {
|
||||
$http.get('phones/phones.json').success(function(data) {
|
||||
$scope.phones = data;
|
||||
});
|
||||
|
||||
$scope.orderProp = 'age';
|
||||
}
|
||||
|
||||
//PhoneListCtrl.$inject = ['$scope', '$http'];
|
||||
</pre>
|
||||
|
||||
<p><code>$http</code> makes an HTTP GET request to our web server, asking for <code>phone/phones.json</code> (the url is
|
||||
relative to our <code>index.html</code> 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.)</p>
|
||||
|
||||
<p>The <code>$http</code> service returns a <a href="api/ng.$q"><code>promise object</code></a> with a <code>success</code>
|
||||
method. We call this method to handle the asynchronous response and assign the phone data to the
|
||||
scope controlled by this controller, as a model called <code>phones</code>. Notice that angular detected the
|
||||
json response and parsed it for us!</p>
|
||||
|
||||
<p>To use a service in angular, you simply declare the names of the dependencies you need as arguments
|
||||
to the controller's constructor function, as follows:</p>
|
||||
|
||||
<pre><code>function PhoneListCtrl($scope, $http) {...}
|
||||
</code></pre>
|
||||
|
||||
<p>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).</p>
|
||||
|
||||
<p>Note that the names of arguments are significant, because the injector uses these to look up the
|
||||
dependencies.</p>
|
||||
|
||||
<p><img class="diagram" src="img/tutorial/xhr_service_final.png"></p>
|
||||
|
||||
<h4>'$' Prefix Naming Convention</h4>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<h4>A Note on Minification</h4>
|
||||
|
||||
<p>Since angular infers the controller's dependencies from the names of arguments to the controller's
|
||||
constructor function, if you were to <a href="http://en.wikipedia.org/wiki/Minification_(programming)">minify</a> the JavaScript code for <code>PhoneListCtrl</code> controller, all of its function arguments would be
|
||||
minified as well, and the dependency injector would not be able to identify services correctly.</p>
|
||||
|
||||
<p>To overcome issues caused by minification, just assign an array with service identifier strings
|
||||
into the <code>$inject</code> property of the controller function, just like the last line in the snippet
|
||||
(commented out) suggests:</p>
|
||||
|
||||
<pre><code>PhoneListCtrl.$inject = ['$scope', '$http'];
|
||||
</code></pre>
|
||||
|
||||
<p>There is also one more way to specify this dependency list and avoid minification issues — using the
|
||||
bracket notation which wraps the function to be injected into an array of strings (representing the
|
||||
dependency names) followed by the function to be injected:</p>
|
||||
|
||||
<pre><code>var PhoneListCtrl = ['$scope', '$http', function($scope, $http) { /* constructor body */ }];
|
||||
</code></pre>
|
||||
|
||||
<p>Both of these methods work with any function that can be injected by Angular, so it's up to your
|
||||
project's style guide to decide which one you use.</p>
|
||||
|
||||
<h3>Test</h3>
|
||||
|
||||
<p><strong><code>test/unit/controllersSpec.js</code>:</strong></p>
|
||||
|
||||
<p>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 <code>new</code> operator and provide the
|
||||
constructor with some kind of fake <code>$http</code> 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:</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
describe('PhoneCat controllers', function() {
|
||||
|
||||
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});
|
||||
}));
|
||||
</pre>
|
||||
|
||||
<p>Note: Because we loaded Jasmine and <code>angular-mocks.js</code> in our test environment, we got two helper
|
||||
methods <a href="api/angular.mock.module"><code>module</code></a> and <a href="api/angular.mock.inject"><code>inject</code></a> that we'll
|
||||
use to access and configure the injector.</p>
|
||||
|
||||
<p>We created the controller in the test environment, as follows:</p>
|
||||
|
||||
<ul>
|
||||
<li><p>We used the <code>inject</code> helper method to inject instances of
|
||||
<a href="api/ng.$rootScope"><code>$rootScope</code></a>,
|
||||
<a href="api/ng.$controller"><code>$controller</code></a> and
|
||||
<a href="api/ng.$httpBackend"><code>$httpBackend</code></a> services into the Jasmine's <code>beforeEach</code>
|
||||
function. These instances come from an injector which is recreated from scratch for every single
|
||||
test. This guarantees that each test starts from a well known starting point and each test is
|
||||
isolated from the work done in other tests.</p></li>
|
||||
<li><p>We created a new scope for our controller by calling <code>$rootScope.$new()</code></p></li>
|
||||
<li><p>We called the injected <code>$controller</code> function passing the <code>PhoneListCtrl</code> function and the created
|
||||
scope as parameters.</p></li>
|
||||
</ul>
|
||||
|
||||
<p>Because our code now uses the <code>$http</code> service to fetch the phone list data in our controller, before
|
||||
we create the <code>PhoneListCtrl</code> child scope, we need to tell the testing harness to expect an
|
||||
incoming request from the controller. To do this we:</p>
|
||||
|
||||
<ul>
|
||||
<li><p>Request <code>$httpBackend</code> service to be injected into our <code>beforeEach</code> function. This is a mock
|
||||
version of the service that in a production environment facilitates all XHR and JSONP requests.
|
||||
The mock version of this service allows you to write tests without having to deal with
|
||||
native APIs and the global state associated with them — both of which make testing a nightmare.</p></li>
|
||||
<li><p>Use the <code>$httpBackend.expectGET</code> method to train the <code>$httpBackend</code> service to expect an incoming
|
||||
HTTP request and tell it what to respond with. Note that the responses are not returned until we call
|
||||
the <code>$httpBackend.flush</code> method.</p></li>
|
||||
</ul>
|
||||
|
||||
<p>Now, we will make assertions to verify that the <code>phones</code> model doesn't exist on <code>scope</code> before
|
||||
the response is received:</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
it('should create "phones" model with 2 phones fetched from xhr', function() {
|
||||
expect(scope.phones).toBeUndefined();
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(scope.phones).toEqual([{name: 'Nexus S'},
|
||||
{name: 'Motorola DROID'}]);
|
||||
});
|
||||
</pre>
|
||||
|
||||
<ul>
|
||||
<li><p>We flush the request queue in the browser by calling <code>$httpBackend.flush()</code>. This causes the
|
||||
promise returned by the <code>$http</code> service to be resolved with the trained response.</p></li>
|
||||
<li><p>We make the assertions, verifying that the phone model now exists on the scope.</p></li>
|
||||
</ul>
|
||||
|
||||
<p>Finally, we verify that the default value of <code>orderProp</code> is set correctly:</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
it('should set the default value of orderProp model', function() {
|
||||
expect(scope.orderProp).toBe('age');
|
||||
});
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
|
||||
<p>You should now see the following output in the Testacular tab:</p>
|
||||
|
||||
<pre><code> Chrome 22.0: Executed 2 of 2 SUCCESS (0.028 secs / 0.007 secs)
|
||||
</code></pre>
|
||||
|
||||
<h2>Experiments</h2>
|
||||
|
||||
<ul>
|
||||
<li><p>At the bottom of <code>index.html</code>, add a <code>{{phones | json}}</code> binding to see the list of phones
|
||||
displayed in json format.</p></li>
|
||||
<li><p>In the <code>PhoneListCtrl</code> controller, pre-process the http response by limiting the number of phones
|
||||
to the first 5 in the list. Use the following code in the $http callback:</p>
|
||||
|
||||
<pre><code> $scope.phones = data.splice(0, 5);
|
||||
</code></pre></li>
|
||||
</ul>
|
||||
|
||||
<h2>Summary</h2>
|
||||
|
||||
<p>Now that you have learned how easy it is to use angular services (thanks to Angular's dependency
|
||||
injection), go to <a href="tutorial/step_06">step 6</a>, where you will add some
|
||||
thumbnail images of phones and some links.</p>
|
||||
|
||||
<ul doc-tutorial-nav="5"></ul></div>
|
Loading…
Add table
Add a link
Reference in a new issue