diff options
| author | Misko Hevery | 2011-04-29 15:18:27 -0700 | 
|---|---|---|
| committer | Igor Minar | 2011-06-06 22:28:38 -0700 | 
| commit | 11e9572b952e49b01035e956c412d6095533031a (patch) | |
| tree | 04dbf96802f552693d44c541c0d825a2769e3d57 /docs/content/tutorial | |
| parent | b6bc6c2ddf1ae1523ec7e4cb92db209cd6501181 (diff) | |
| download | angular.js-11e9572b952e49b01035e956c412d6095533031a.tar.bz2 | |
Move documentation under individual headings
Diffstat (limited to 'docs/content/tutorial')
| -rw-r--r-- | docs/content/tutorial/index.ngdoc | 172 | ||||
| -rwxr-xr-x | docs/content/tutorial/step_00.ngdoc | 77 | ||||
| -rwxr-xr-x | docs/content/tutorial/step_01.ngdoc | 88 | ||||
| -rwxr-xr-x | docs/content/tutorial/step_02.ngdoc | 137 | ||||
| -rwxr-xr-x | docs/content/tutorial/step_03.ngdoc | 108 | ||||
| -rwxr-xr-x | docs/content/tutorial/step_04.ngdoc | 161 | ||||
| -rwxr-xr-x | docs/content/tutorial/step_05.ngdoc | 147 | ||||
| -rwxr-xr-x | docs/content/tutorial/step_06.ngdoc | 113 | ||||
| -rwxr-xr-x | docs/content/tutorial/step_07.ngdoc | 181 | ||||
| -rwxr-xr-x | docs/content/tutorial/step_08.ngdoc | 148 | ||||
| -rwxr-xr-x | docs/content/tutorial/step_09.ngdoc | 108 | ||||
| -rw-r--r-- | docs/content/tutorial/step_10.ngdoc | 110 | ||||
| -rw-r--r-- | docs/content/tutorial/step_11.ngdoc | 178 | 
13 files changed, 1728 insertions, 0 deletions
| diff --git a/docs/content/tutorial/index.ngdoc b/docs/content/tutorial/index.ngdoc new file mode 100644 index 00000000..b430b248 --- /dev/null +++ b/docs/content/tutorial/index.ngdoc @@ -0,0 +1,172 @@ +@workInProgress +@ngdoc overview +@name Tutorial +@description + +A great way to get introduced to angular is to work through the {@link tutorial.step_00 angular +tutorial}, which walks you through the construction of an angular web app. The app you will build +in the tutorial is loosely based on the {@link http://www.google.com/phone/ Google phone gallery +app}. The {@link http://angular.github.com/angular-phonecat/step-11/app/ end result of our effort} +is visually simpler, but demonstrates many of the angular features without distractions in the +form of CSS code. + +This tutorial app ends up like a Google phone gallery app, but is originally based on the {@link +https://github.com/angular/angular-seed angular-seed project}. The angular seed app isn't +necessary for building angular apps, but it helps you get started quickly and makes the +development and testing process much easier. Angular-seed includes a simple example, the latest +angular libraries, test libraries, and scripts.  It provides all of these in an environment that +is pre-configured for developing a typical web app.  + +Once you set up your tutorial environment, you should be able to get through the material in less +than a day and you'll have fun doing it. More experienced coders may be able to zip through the +exercises in an afternoon. In any case, we promise that your time will be well spent! + +When you finish the tutorial you will be able to: + +* Create a simple dynamic application that works in any browser +* Define the differences between angular and common JavaScript frameworks +* Understand angular expressions +* Understand how data binding works in angular +* Use the angular-seed project to quickly boot-strap your own projects +* Create and run tests +* Identify resources for learning more about angular + +You can work through the tutorial in any of the following ways: + +* <a href="#UsingGit">Using Git</a>. Use the Git versioning system to get the files for each step. +* <a href="#UsingSnapshots">Using Snapshots</a>. Download snapshots (files for each step of the +tutorial) and tinker with them. +* <a href="#ReadingExamples">Reading the Examples</a>. Read through the examples, and inspect +results and code on our server. + +The first two ways (Git and snapshots) give you a fuller experience, in that you can run the unit +and end-to-end tests in addition to the tutorial app.  They also give you the ability to play +around with the code and get instant feedback in your browser.  The last way (reading through the +tutorial online) requires no setup on your machine, but you can't run the tests, and it won't be +as easy to play around with the code. + +<a name="PreReqs"></a> +# Prerequisites for Git and Snapshots + +To run the tutorial app and tests on your machine (using Git or the snapshots) you will need the +following: + +* You need to be running on a Mac or Linux machine. +* An http server running on your system. If you don't already have one installed, you can install +`node.js` ({@link https://github.com/joyent/node/wiki/Installation node.js install}) or another +http sever (such as Apache, etc.). +* Java. This is required for running tests. Angular itself doesn't require Java. +* A modern browser (including IE8+). Needed for viewing and debugging code. +* A text editor of your choice. + +<a name="UsingGit"></a> +# Using Git + +The following instructions are for developers who are comfortable with Git's versioning system: + +1. Check to be sure you have all of the <a href="#PreReqs">prerequisites</a> on your system. + +2. Clone the angular-phonecat repository located at {@link +https://github.com/angular/angular-phonecat angular-phonecat} by running the following command in +a terminal: + +           git clone git://github.com/angular/angular-phonecat.git + +   This will create a directory called `angular-phonecat`. + +3. In terminal, navigate to the `angular-phonecat` directory and run: + +           git checkout step-0 + +   (You can run `git checkout step-[0-11]` to go to any of the steps in the tutorial). + +4. To see the app running in a browser, do the following: +   * __For node.js users:__ +       1. Run `./scripts/web-server.js` to start the app server. +       2. Open a browser window for the app and navigate to http://localhost:8000/app/index.html. + +   * __For other http servers:__ +       1. Configure the server to serve the files in the `angular-phonecat` directory. +       2. Run `./scripts/web-server.js` to start the app server. +       3. Navigate in your browser to +       http://localhost:[*port-number*]/[*context-path*]/app/index.html. + +5. To see tests running in a browser, do the following: +   * __For node.js users:__ +       1. Run `./scripts/test-server.sh` to start the test web server. +       2. Open a browser window for the tests, navigate to http://localhost:9876, and choose +       "strict mode". +   * __For other http servers:__ +       1. Configure the server to serve the files in the `angular-phonecat` directory. +       1. Run `./scripts/test-server.sh` to start the test web server. +       3. Navigate in your browser to http://localhost:[*port-number*]/, and choose "strict mode". + + + +<a name="UsingSnapshots"></a> +# Using Snapshots + +Snapshots are the sets of files that reflect the state of the tutorial app at each step. These +files include the HTML, CSS, and JavaScript for the app, plus Jasmine JavaScript files and Java +libraries for the test stack. These will let you run the tutorial app and tests, without requiring +knowledge of Git.  You can download and install the snapshot files as follows: + +1. Check to be sure you have all of the <a href="#PreReqs">prerequisites</a> on your system. + +2. Navigate to [*the angular server*], and download and unzip [*the snapshot file*] to an +[*install-dir*] of your choosing. + +3. Change directories to [*install-dir*]/sandbox. + +4. Run the following command: +    * `./goto_step.sh 0` + +    You have to start out at the beginning, which is Step 0. After you set up Step 0, you can skip +    around between any steps.  + +1. To see the app running in your browser, do the following: +    * __For node.js users:__ +       1. Run `./scripts/web-server.js` to run the web server. +       2. Open a browser window for the app and navigate to http://localhost:8000/app/index.html. +       3. Open a browser window for the tests, navigate to http://localhost:9876, and choose +       "strict mode". + +   * __For other http servers:__ +       1. Configure servers to serve the app and test files in the [*install-dir*]/sandbox. +       2. Start the server. +       3. Navigate in your app browser to +       http://localhost:[*port-number*]/[*context-path*]/app/index.html. +       4. Navigate in your test browser to http://localhost:[*port-number*] and choose "strict +       mode". + +1. To view the tutorial app at different steps, run `./goto_step.sh [0-11]` and then refresh your +browser. For example, say you're on Step 5 of the tutorial, and you want to see the app in action: + +    1. Run `goto_step.sh 5` from the command line in the `sandbox` directory. +    1. Refresh your app browser. + +<a name="ReadingExamples"></a> +# Reading the Examples + +If you don't want to set up anything on your local machine, you can read through the tutorial and +inspect the tutorial files on our servers; doing this will give you a good idea of what angular +does, but you won't be able to make any code changes and experiment on your own. + +To see the running app at each tutorial step, click the "Example" link at the top or bottom of +each tutorial page.  + +To view the code differences between tutorial steps, click the Code Diff link at top or bottom of +each tutorial page. Additions are highlighted in green; deletions are highlighted in red.  + + +# Relative URLs +Throughout the tutorial, we use relative URLs to refer to files hosted on our local http server. +The absolute URL depends on your configuration. For example, if you are using the node.js server, +`app/index.html` translates to: + +       http://localhost:8000/app/index.html + +If you are using your own http server running on port 8080 and the tutorial files are hosted at +`/angular_tutorial`, `app/index.html` translates to: + +       http://localhost:8080/angular_tutorial/app/index.html diff --git a/docs/content/tutorial/step_00.ngdoc b/docs/content/tutorial/step_00.ngdoc new file mode 100755 index 00000000..e506fcaf --- /dev/null +++ b/docs/content/tutorial/step_00.ngdoc @@ -0,0 +1,77 @@ +@workInProgress
 +@ngdoc overview
 +@name Tutorial: Step 0
 +@description
 +
 +<table id="tutorial_nav">
 +<tr>
 +<td id="previous_step">{@link tutorial Previous}</td>
 +<td id="step_result">{@link  http://angular.github.com/angular-phonecat/step-0/app Example}</td>
 +<td id="tut_home">{@link tutorial Tutorial Home}</td>
 +<td id="code_diff">Code Diff</td>
 +<td id="next_step">{@link tutorial.step_01 Next}</td>
 +</tr>
 +</table>
 +
 +The following sample code is our starting point.  It is a static HTML page that displays next to
 +nothing, but it has everything we need to proceed. You can think of this bit of code as our
 +prototype template, consisting of basic HTML tags with a pair of angular specific attributes.
 +
 +__`app/index.html`:__
 +<pre>
 +<!doctype html>
 +<html xmlns:ng="http://angularjs.org/">
 +<head>
 +  <meta charset="utf-8">
 +  <title>my angular app</title>
 +  <link rel="stylesheet" href="css/app.css"/>
 +</head>
 +<body>
 +
 +  Nothing here yet!
 +
 +  <script src="lib/angular/angular.js" ng:autobind></script>
 +</body>
 +</html>
 +</pre>
 +
 +## Discussion:
 +
 +Although our app doesn't appear to do anything dynamic, note the following:
 +
 +* __... `xmlns:ng="http://angularjs.org"` ...__  This `xmlns` declaration for the `ng` namespace
 +must be specified if you use XHTML, or if you are targeting IE older than 9 (regardless of whether
 +you are using XHTML or HTML).
 +
 +* __`<script src="lib/angular/angular.js"` ...__ This downloads the `angular.js` script and
 +registers a callback that will be executed by the browser when the containing HTML page is fully
 +downloaded. When the callback is executed, angular looks for the {@link
 +angular.directive.ng:autobind ng:autobind} attribute. If `ng:autobind` is found, it signals
 +angular to bootstrap and compile and manage the whole html page.
 +
 +    Note: If you elected not to download any tutorial files but still want to try out some angular
 +    code on your system, you can change the relative path to the `angular.js` script in your
 +    template from `./lib/angular/angular.js` to the following:
 +
 +            <script src="http://code.angularjs.org/angular-0.9.14.js" ng:autobind></script>
 +
 +    This will download the angular script from the angular server instead of from a local file.
 +
 +* To try this code out in your browser, you need to navigate to the step-0 page (you are currently
 +on Step 0 of the tutorial). If your http server is running, navigate to `app/index.html`.
 +Remember, this is a relative URL (see the Relative URL section in {@link tutorial Tutorial}). The
 +browser will display the same thing as you would see if you go to
 +http://angular.github.com/angular-phonecat/step-0/app (accessible from Example link at the bottom
 +of the page).
 +
 +Now we can move on and add some content to our developing web app.
 +
 +<table id="tutorial_nav">
 +<tr>
 +<td id="previous_step">{@link tutorial Previous}</td>
 +<td id="step_result">{@link  http://angular.github.com/angular-phonecat/step-0/app Example}</td>
 +<td id="tut_home">{@link tutorial Tutorial Home}</td>
 +<td id="code_diff">Code Diff</td>
 +<td id="next_step">{@link tutorial.step_01 Next}</td>
 +</tr>
 +</table>
 diff --git a/docs/content/tutorial/step_01.ngdoc b/docs/content/tutorial/step_01.ngdoc new file mode 100755 index 00000000..e22adc20 --- /dev/null +++ b/docs/content/tutorial/step_01.ngdoc @@ -0,0 +1,88 @@ +@workInProgress
 +@ngdoc overview
 +@name Tutorial: Step 1
 +@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-1/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-0...step-1 Code Diff}</td>
 +    <td id="next_step">{@link tutorial.step_02 Next}</td>
 +  </tr>
 +</table>
 +
 +Now that we have the basic ingredients in place, let's add some basic information about two cell
 +phones to our app.
 +
 +Note: We will usually include only the new code that we added for each step.  In this and
 +subsequent examples, we will leave out code from the previous step that hasn't changed, for
 +example:
 +
 +        ...
 +        <html xmlns:ng="http://angularjs.org">
 +        ...
 +
 +Let's add the following code to `index.html`:
 +
 +__`app/index.html`:__
 +<pre>
 +<head>
 +...
 +  <title>Google Phone Gallery</title>
 +...
 +</head>
 +...
 +  <ul>
 +    <li>
 +      <span>Nexus S<span>
 +      <p>
 +        Fast just got faster with Nexus S.
 +      </p>
 +    </li>
 +    <li>
 +      <span>Motorola XOOM™ with Wi-Fi<span>
 +      <p>
 +        The Next, Next Generation tablet.
 +      </p>
 +    </li>
 +  </ul>
 +...
 +</pre>
 +
 +## Discussion:
 +
 +* It's a static web page! We displayed info about two phones! Yay.
 +
 +* For those of you playing along at home on your own web servers, did you switch to Step 1 and
 +refresh your browsers?
 +
 +    * __{@link tutorial Using Git:}__
 +
 +        From your `angular-phonecat` directory, run this command:
 +
 +                git checkout step-1
 +
 +    * __{@link tutorial Using Snapshots:}__
 +
 +        From `[install directory]/sandbox`, run this command:
 +
 +                ./goto_step.sh 1
 +
 +* Now would be a good time to open up `app/index.html` in your browser and see the current state
 +of our "application". It's not very exciting, but that's ok.
 +
 +When you're ready, let's move on and start using some angular features to turn this static page
 +into a dynamic web app.
 +
 +<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-1/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-0...step-1 Code Diff}</td>
 +    <td id="next_step">{@link tutorial.step_02 Next}</td>
 +  </tr>
 +</table>
 diff --git a/docs/content/tutorial/step_02.ngdoc b/docs/content/tutorial/step_02.ngdoc new file mode 100755 index 00000000..50fbd240 --- /dev/null +++ b/docs/content/tutorial/step_02.ngdoc @@ -0,0 +1,137 @@ +@workInProgress
 +@ngdoc overview
 +@name Tutorial: Step 2
 +@description
 +<table id="tutorial_nav">
 + <tr>
 +   <td id="previous_step">{@link tutorial.step_01 Previous}</td>
 +   <td id="step_result">{@link  http://angular.github.com/angular-phonecat/step-2/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-1...step-2 Code
 +Diff}</td>
 +   <td id="next_step">{@link tutorial.step_03 Next}</td>
 + </tr>
 +</table>
 +
 +In the last step, we remembered what a basic, static web page looks like, and now we want to get
 +dynamic. There are many ways to do this, but an important feature of angular is the incorporation
 +of the principles behind {@link http://en.wikipedia.org/wiki/Model–View–Controller the MVC design
 +pattern} into client-side web apps. With that in mind, let's use a little angular and a little
 +JavaScript to add Model, View, and Controller components to our app, and change the static page
 +into one that is dynamically generated.
 +
 +Our __View__ component is constructed by angular from this template:
 +
 +__`app/index.html`:__
 +<pre>
 +...
 +<body ng:controller="PhoneListCtrl">
 +
 +  <ul>
 +    <li ng:repeat="phone in phones">
 +      {{phone.name}}
 +      <p>{{phone.snippet}}</p>
 +    </li>
 +  </ul>
 +
 +  <script src="lib/angular/angular.js" ng:autobind></script>
 +  <script src="js/controllers.js"></script>
 +</body>
 +...
 +</pre>
 +
 +Our data __Model__ (a short list of phones in object literal notation) is instantiated within our
 +__Controller__ function (`PhoneListCtrl`):
 +
 +__`app/js/controllers.js`:__
 +<pre>
 +/* App Controllers */
 +
 +function PhoneListCtrl() {
 +  this.phones = [{"name": "Nexus S",
 +                  "snippet": "Fast just got faster with Nexus S."},
 +                 {"name": "Motorola XOOM™ with Wi-Fi",
 +                  "snippet": "The Next, Next Generation tablet."},
 +                 {"name": "MOTOROLA XOOM™",
 +                  "snippet": "The Next, Next Generation tablet."}];
 +}
 +</pre>
 +
 +The "Angular way" urges us to test as we develop:
 +
 +__`test/unit/controllersSpec.js`:__
 +<pre>
 +/* jasmine specs for controllers go here */
 +describe('PhoneCat controllers', function() {
 +
 +  describe('PhoneListCtrl', function(){
 +
 +    it('should create "phones" model with 3 phones', function() {
 +      var ctrl = new PhoneListCtrl();
 +      expect(ctrl.phones.length).toBe(3);
 +    });
 +  });
 +});
 +</pre>
 +
 +## Discussion:
 +
 +So what were our changes from Step 1?
 +
 +* __View template:__ We replaced the hard-coded phone list with the {@link
 +angular.widget.@ng:repeat ng:repeat widget} and two {@link guide.expression angular expressions}
 +enclosed in curly braces: `{{phone.name}}` and `{{phone.snippet}}`:
 +
 +    * The `ng:repeat="phone in phones"` statement in the `<li>` tag is an angular repeater.  It
 +    tells angular to create a `<li>` element for each phone in the phones list, using the first
 +    `<li>` tag as the template.
 +
 +    * The curly braces around `phone.name` and `phone.snippet` are an example of {@link
 +    angular.markup angular markup}. The curly braces are shorthand for the angular directive
 +    {@link angular.directive.ng:bind ng:bind}.  They indicate to angular that these are template
 +    binding points. Binding points are locations in the template where angular creates
 +    data-binding between the View and the Model. In angular, the View is a projection of the Model
 +    through the HTML template. This means that whenever the model changes, angular refreshes the
 +    appropriate binding points, which updates the view.
 +
 +* __Controller:__  At this point, it doesn't appear as if our controller is doing very much
 +controlling, but it is playing a crucial role: providing context for our data model so we can
 +establish data-binding between the model and the view.  Note in the following how we connected the
 +dots between our presentation, data, and logic components:
 +
 +    * The name of our controller function (in the JavaScript file `controllers.js`) matches the
 +    {@link angular.directive.ng:controller ng:controller} directive in the `<body>` tag
 +    (`PhoneListCtrl`).
 +    * We instantiated our data within the scope of our controller function, and our template
 +    binding points are located within the block bounded by the `<body
 +    ng:controller="PhoneListCtrl>` tag.
 +
 +    Angular uses scopes, along with the information contained in the template, data model, and
 +    controller to keep the Model and View separated but in sync: any changes to the model are
 +    reflected in the view; any changes that occur in the view are reflected in the model.
 +
 +* __Model:__ For our data model, we created a simple array of phone records, specified in object
 +literal notation.
 +
 +* __Testing:__ Ease of testing is another cornerstone of angular's design philosophy.  All we are
 +doing here is showing how easy it is to create a unit test using the technology baked into
 +angular.  The test verifies that we have 3 records in the phones array.
 +
 +    To run this test, make sure you have a {@link tutorial test server running}, and type
 +    `./scripts/test.sh` from the command line.
 +
 +    Angular developers prefer the syntax of Jasmine's Behavior-driven Development (BDD) framework
 +    when writing tests. So while Jasmine is not required by angular, we use it to write all tests
 +    in this tutorial. You can learn about Jasmine on the {@link http://pivotal.github.com/jasmine/
 +    Jasmine home page} and on the {@link https://github.com/pivotal/jasmine/wiki Jasmine wiki}.
 +
 +<table id="tutorial_nav">
 + <tr>
 +   <td id="previous_step">{@link tutorial.step_01 Previous}</td>
 +   <td id="step_result">{@link  http://angular.github.com/angular-phonecat/step-2/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-1...step-2 Code
 +Diff}</td>
 +   <td id="next_step">{@link tutorial.step_03 Next}</td>
 + </tr>
 +</table>
 diff --git a/docs/content/tutorial/step_03.ngdoc b/docs/content/tutorial/step_03.ngdoc new file mode 100755 index 00000000..4333636d --- /dev/null +++ b/docs/content/tutorial/step_03.ngdoc @@ -0,0 +1,108 @@ +@workInProgress
 +@ngdoc overview
 +@name Tutorial: Step 3
 +@description
 +<table id="tutorial_nav">
 +<tr>
 +  <td id="previous_step">{@link tutorial.step_02 Previous}</td>
 +  <td id="step_result">{@link  http://angular.github.com/angular-phonecat/step-3/app Example}</td>
 +  <td id="tut_home">{@link tutorial Tutorial Home}</td>
 +  <td id="code_diff">{@link
 +  https://github.com/angular/angular-phonecat/commit/a03815f8fb00217f5f9c1d3ef83282f79818e706 Code
 +  Diff}</td>
 +  <td id="next_step">{@link tutorial.step_04 Next}</td>
 +</tr>
 +</table>
 +
 +We did a lot of work in laying the foundation of our app in the last step, so now we'll do
 +something simple, and add full text search.  We will also write an end-to-end test, because a good
 +end-to-end test is a good friend. It stays with your app, keeps an eye on it, and quickly detects
 +regressions.
 +
 +__`app/index.html`:__
 +<pre>
 +...
 +   Fulltext Search: <input name="query"/>
 +
 +  <ul class="phones">
 +    <li ng:repeat="phone in phones.$filter(query)">
 +      {{phone.name}}
 +      <p>{{phone.snippet}}</p>
 +    </li>
 +  </ul>
 +...
 +</pre>
 +__`test/e2e/scenarios.js`:__
 +<pre>
 +/* jasmine-like end2end tests go here */
 +describe('PhoneCat App', function() {
 +
 +  describe('Phone list view', function() {
 +
 +    beforeEach(function() {
 +      browser().navigateTo('../../app/index.html');
 +    });
 +
 +
 +    it('should filter the phone list as user types into the search box', function() {
 +      expect(repeater('.phones li').count()).toBe(3);
 +
 +      input('query').enter('nexus');
 +      expect(repeater('.phones li').count()).toBe(1);
 +
 +      input('query').enter('motorola');
 +      expect(repeater('.phones li').count()).toBe(2);
 +    });
 +  });
 +});
 +</pre>
 +
 +## Discussion:
 +
 +We continued using the same controller that we set up in Step 2, but we added the following
 +features to our app:
 +
 +* __Search Box:__  A standard HTML `<input>` tag combined with angular's {@link
 +angular.Array.filter $filter} utility (added to the repeater) lets a user type in search criteria
 +and immediately see the effects of their search on the phone list.  This new code demonstrates the
 +following:
 +
 +   * Two way Data-binding.  This is one of the core features in angular.  When the page loads,
 +   angular binds the name of the input box to a variable of the same name in the data model and
 +   keeps the two in sync.
 +
 +In this example, the data that you type into the input box (named __`query`__) is immediately
 +available as a filter input in the list repeater (`phone in phones.$filter(`__`query`__`)`).
 +Whenever the data model changes and this change causes the input to the repeater to change, the
 +repeater will efficiently update the DOM to reflect the current state of the model.
 +
 +   * Use of `$filter` in a template.  The `$filter` function is one of several built-in {@link
 +   angular.Array angular functions} that augment JavaScript arrays during their evaluation as
 +   angular expressions. In {@link guide.expression angular expressions}, these array utilities are
 +   available as array methods. (They are prefixed with a $ to avoid naming collisions.)
 +
 +   * `ng:repeat` automatically shrinks and grows the number of phones in the View, via DOM
 +   manipulation that is completely transparent to the developer.  If you've written any DOM
 +   manipulation code, this should make you happy.
 +
 +* __CSS:__ We added in some minimal CSS to the file we set up in Step 0: `./css/app.css`.
 +
 +* __Testing:__  To run the end to end test, open http://localhost:8000/test/e2e/runner.html in
 +your browser. This end-to-end test shows the following:
 +
 +    * Proof that the search box and the repeater are correctly wired together.
 +
 +    * How easy it is to write end-to-end tests.  This is just a simple test, but the point here is
 +    to show how easy it is to set up a functional, readable, end-to-end test.
 +
 +<table id="tutorial_nav">
 +<tr>
 +  <td id="previous_step">{@link tutorial.step_02 Previous}</td>
 +  <td id="step_result">{@link  http://angular.github.com/angular-phonecat/step-3/app Example}</td>
 +  <td id="tut_home">{@link tutorial Tutorial Home}</td>
 +  <td id="code_diff">{@link
 +  https://github.com/angular/angular-phonecat/commit/a03815f8fb00217f5f9c1d3ef83282f79818e706 Code
 +  Diff}</td>
 +  <td id="next_step">{@link tutorial.step_04 Next}</td>
 +</tr>
 +</table>
 diff --git a/docs/content/tutorial/step_04.ngdoc b/docs/content/tutorial/step_04.ngdoc new file mode 100755 index 00000000..0589ba75 --- /dev/null +++ b/docs/content/tutorial/step_04.ngdoc @@ -0,0 +1,161 @@ +@workInProgress
 +@ngdoc overview
 +@name Tutorial: Step 4
 +@description
 +<table id="tutorial_nav">
 +<tr>
 +<td id="previous_step">{@link tutorial.step_03 Previous}</td>
 +<td id="step_result">{@link  http://angular.github.com/angular-phonecat/step-4/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-3...step-4 Code
 +Diff}</td>
 +<td id="next_step">{@link tutorial.step_05 Next}</td>
 +</tr>
 +</table>
 +
 +In this step, we add a feature that lets our users choose which way to order the phone list.
 +
 +__`app/index.html`:__
 +<pre>
 +...
 +  <ul class="predicates">
 +    <li>
 +      Search: <input type="text" name="query"/>
 +    </li>
 +    <li>
 +      Sort by:
 +      <select name="orderProp">
 +        <option value="name">Alphabetical</option>
 +        <option value="age">Newest</option>
 +      </select>
 +    </li>
 +  </ul>
 +
 +  <ul class="phones">
 +    <li ng:repeat="phone in phones.$filter(query).$orderBy(orderProp)">
 +      {{phone.name}}
 +      <p>{{phone.snippet}}</p>
 +    </li>
 +  </ul>
 +...
 +</pre>
 +
 +__`app/js/controller.js`:__
 +<pre>
 +/* App Controllers */
 +
 +function PhoneListCtrl() {
 +  this.phones = [{"name": "Nexus S",
 +                  "snippet": "Fast just got faster with Nexus S.",
 +                  "age": 0},
 +                 {"name": "Motorola XOOM™ with Wi-Fi",
 +                  "snippet": "The Next, Next Generation tablet.",
 +                  "age": 1},
 +                 {"name": "MOTOROLA XOOM™",
 +                  "snippet": "The Next, Next Generation tablet.",
 +                  "age": 2}];
 +
 +  this.orderProp = 'age';
 +}
 +</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() {
 +      ctrl = new PhoneListCtrl();
 +    });
 +
 +
 +    it('should create "phones" model with 3 phones', function() {
 +      expect(ctrl.phones.length).toBe(3);
 +    });
 +
 +
 +    it('should set the default value of orderProp model', function() {
 +      expect(ctrl.orderProp).toBe('age');
 +    });
 +  });
 +});
 +</pre>
 +
 +__`test/e2e/scenarios.js`:__
 +<pre>
 +/* jasmine-like end2end tests go here */
 +describe('PhoneCat App', function() {
 +
 +  describe('Phone list view', function() {
 +
 +    beforeEach(function() {
 +      browser().navigateTo('../../app/index.html');
 +    });
 +
 +
 +    it('should filter the phone list as user types into the search box', function() {
 +      expect(repeater('.phones li').count()).toBe(3);
 +
 +      input('query').enter('nexus');
 +      expect(repeater('.phones li').count()).toBe(1);
 +
 +      input('query').enter('motorola');
 +      expect(repeater('.phones li').count()).toBe(2);
 +    });
 +
 +
 +    it('should be possible to control phone order via the drop down select box', function() {
 +      input('query').enter('tablet'); //let's narrow the dataset to make the test assertions
 +      shorter
 +
 +      expect(repeater('.phones li', 'Phone List').column('a')).
 +          toEqual(["Motorola XOOM\u2122 with Wi-Fi",
 +                   "MOTOROLA XOOM\u2122"]);
 +
 +      select('orderProp').option('alphabetical');
 +
 +      expect(repeater('.phones li', 'Phone List').column('a')).
 +          toEqual(["MOTOROLA XOOM\u2122",
 +                   "Motorola XOOM\u2122 with Wi-Fi"]);
 +    });
 +  });
 +});
 +</pre>
 +
 +## Discussion:
 +
 +To provide dynamic ordering, we employ another one of angular's "array type augmenters" and let
 +the data binding do the rest of the work for us:
 +
 +* First, we provide a `<select>` element named `orderProp` for our users so they can choose to
 +sort the phone list either alphabetically or by the age of the phone.  We added the `age` property
 +to each phone record so we can sort by that field.
 +
 +* Like {@link angular.Array.filter $filter}, {@link angular.Array.orderBy $orderBy} is a built-in
 +method available on array objects in angular expressions.  In our UI template, we set up a select
 +box that lets the user set the `orderProp` model variable to one of the string constants: `age` or
 +`name`.
 +
 +* In our controller, we added a line to set the default value of `orderProp` to `age`.  If we
 +don't override the default value, angular uses the value of the first `<option>` element when it
 +initializes the data model.
 +
 +* Our unit test now verifies that our default ordering property is set.
 +
 +* We added an end-to-end test to verify that our select box ordering mechanism works properly.
 +
 +* Once again we added a little more CSS to improve the View.
 +
 +<table id="tutorial_nav">
 +<tr>
 +<td id="previous_step">{@link tutorial.step_03 Previous}</td>
 +<td id="step_result">{@link  http://angular.github.com/angular-phonecat/step-4/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-3...step-4 Code
 +Diff}</td>
 +<td id="next_step">{@link tutorial.step_05 Next}</td>
 +</tr>
 +</table>
 diff --git a/docs/content/tutorial/step_05.ngdoc b/docs/content/tutorial/step_05.ngdoc new file mode 100755 index 00000000..8ec0fca4 --- /dev/null +++ b/docs/content/tutorial/step_05.ngdoc @@ -0,0 +1,147 @@ +@workInProgress
 +@ngdoc overview
 +@name Tutorial: Step 5
 +@description
 +<table id="tutorial_nav">
 +<tr>
 + <td id="previous_step">{@link tutorial.step_04 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_06 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_04 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_06 Next}</td>
 +</tr>
 +</table>
 diff --git a/docs/content/tutorial/step_06.ngdoc b/docs/content/tutorial/step_06.ngdoc new file mode 100755 index 00000000..afe809a6 --- /dev/null +++ b/docs/content/tutorial/step_06.ngdoc @@ -0,0 +1,113 @@ +@workInProgress
 +@ngdoc overview
 +@name Tutorial: Step 6
 +@description
 +<table id="tutorial_nav">
 +<tr>
 +<td id="previous_step">{@link tutorial.step_05 Previous}</td>
 +<td id="step_result">{@link  http://angular.github.com/angular-phonecat/step-6/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-5...step-6 Code
 +Diff}</td>
 +<td id="next_step">{@link tutorial.step_07 Next}</td>
 +</tr>
 +</table>
 +
 +In this step, we add thumbnail images, links, and a little more CSS to our app.  For now, our
 +links go nowhere.  One step at a time; in the next step we'll implement new views that these links
 +will open.
 +
 +__`app/index.html`:__
 +<pre>
 +...
 +  <ul class="predicates">
 +    <li>
 +      Search: <input type="text" name="query"/>
 +    </li>
 +    <li>
 +      Sort by:
 +      <select name="orderProp">
 +        <option value="name">Alphabetical</option>
 +        <option value="age">Newest</option>
 +      </select>
 +    </li>
 +  </ul>
 +
 +  <ul class="phones">
 +    <li ng:repeat="phone in phones.$filter(query).$orderBy(orderProp)">
 +      <a href="#/phones/{{phone.id}}">{{phone.name}}</a>
 +      <a href="#/phones/{{phone.id}}" class="thumb"><img ng:src="{{phone.imageUrl}}"></a>
 +      <p>{{phone.snippet}}</p>
 +    </li>
 +  </ul>
 +...
 +</pre>
 +
 +__`app/js/controller.js`__ (Unchanged):
 +<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>
 +
 +__`app/phones/phones.json`__ (sample snippet):
 +<pre>
 + [
 +  {
 +   "age": 4,
 +   ...
 +   "carrier": "T-Mobile",
 +   "id": "motorola-defy-with-motoblur",
 +   "imageUrl": "http://google.com/phone/image/small/640001",
 +   "name": "Motorola DEFY\u2122 with MOTOBLUR\u2122",
 +   "snippet": "Are you ready for everything life throws your way?"
 +  },
 +  …
 + ]
 +</pre>
 +
 +__`test/e2e/scenarios.js`__:
 +<pre>
 +...
 +    it('should render phone specific links', function() {
 +      input('query').enter('nexus');
 +      element('.phones li a').click();
 +      expect(browser().location().hash()).toBe('/phones/nexus-s');
 +    });
 +...
 +</pre>
 +
 +## Discussion:
 +
 +* Note that we're using {@link guide.expression angular expressions} enclosed in the now-familiar
 +{@link angular.markup double-curly brace markup} in the href attribute values. These represent
 +attribute bindings, and work the same way as the bindings we saw in previous steps.
 +
 +* Note also the use of the {@link angular.directive.ng:src ng:src} directive in the `<img>` tag.
 +That directive prevents the browser from treating the angular `{{ exppression }}` markup
 +literally, as it would do if we tried to use markup in a regular `src` attribute.  Use `ng:src` to
 +keep the browser from eagerly making an extra http request to an invalid location.
 +
 +* We expanded our end-to-end test to verify that the app is generating correct links to the phone
 +views we will implement in the upcoming steps.
 +
 +<table id="tutorial_nav">
 +<tr>
 +<td id="previous_step">{@link tutorial.step_05 Previous}</td>
 +<td id="step_result">{@link  http://angular.github.com/angular-phonecat/step-6/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-5...step-6 Code
 +Diff}</td>
 +<td id="next_step">{@link tutorial.step_07 Next}</td>
 +</tr>
 +</table>
 diff --git a/docs/content/tutorial/step_07.ngdoc b/docs/content/tutorial/step_07.ngdoc new file mode 100755 index 00000000..802130c5 --- /dev/null +++ b/docs/content/tutorial/step_07.ngdoc @@ -0,0 +1,181 @@ +@workInProgress
 +@ngdoc overview
 +@name Tutorial: Step 7
 +@description
 +<table id="tutorial_nav">
 +<tr>
 +<td id="previous_step">{@link tutorial.step_06 Previous}</td>
 +<td id="step_result">{@link  http://angular.github.com/angular-phonecat/step-7/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-6...step-7 Code
 +Diff}</td>
 +<td id="next_step">{@link tutorial.step_08 Next}</td>
 +</tr>
 +</table>
 +
 +Our app is slowly growing and becoming more complex. Up until now, the app provided our users with
 +just one view (the list of all phones), and all of our template code was located in the
 +`index.html` file.  The next step in building our app is the addition of a view that will show
 +detailed information about each of the devices in our list.
 +
 +To add the detailed view, we could expand the `index.html` file to contain template code for both
 +views, but that would get messy very quickly. Instead, we are going to turn the `index.html`
 +template into what we call a "layout template". This is a template that is common for all views in
 +our application.   Other "partial templates" are then included into this layout template depending
 +on the current "route" — the view that is currently displayed to the user.
 +
 +Similarly as with templates, angular also allows for controllers and scopes managed by these
 +controllers to be nested. We are going to create a "root" controller called `PhoneCatCtrl`, which
 +will contain the declaration of routes for the application.
 +
 +Application routes in angular are declared via the {@link angular.service.$route $route} service.
 +This services makes it easy to wire together controllers, View templates, and the current URL
 +location in the browser.  Using this feature we can implement {@link
 +http://en.wikipedia.org/wiki/Deep_linking deep linking}, which lets us utilize the browser's
 +History, and Back and Forward browser navigation.
 +
 +We'll use the $route service to declare that our application consists of two different views: one
 +view presents the phone listing, and the other view presents the details for a particular phone.
 +Each view will have the template stored in a separate file in the `app/partials/` directory.
 +Similarly each view will have a controller associated with it. These will be stored in the
 +existing `app/js/controllers.js` file.
 +
 +The `$route` service is usually used in conjunction with the {@link angular.widget.ng:view
 +ng:view} widget. The role of the `ng:view` widget is to include the view template for the current
 +route into the layout template, which makes it a perfect fit for our `index.html` template.
 +
 +For now we are going to get all the routing going, and move the phone listing template into a
 +separate file. We'll save the implementation of the phone details View for the next step.
 +
 +__`app/index.html`:__
 +<pre>
 +...
 +<body ng:controller="PhoneCatCtrl">
 +
 +  <ng:view></ng:view>
 +
 +  <script src="lib/angular/angular.js" ng:autobind></script>
 +  <script src="js/controllers.js"></script>
 +</body>
 +</html>
 +</pre>
 +
 +__`app/partials/phone-list.html`:__
 +<pre>
 +<ul class="predicates">
 +  <li>
 +    Search: <input type="text" name="query"/>
 +  </li>
 +  <li>
 +    Sort by:
 +    <select name="orderProp">
 +      <option value="name">Alphabetical</option>
 +      <option value="age">Newest</option>
 +    </select>
 +  </li>
 +</ul>
 +
 +<ul class="phones">
 +  <li ng:repeat="phone in phones.$filter(query).$orderBy(orderProp)">
 +    <a href="#/phones/{{phone.id}}">{{phone.name}}</a>
 +    <a href="#/phones/{{phone.id}}" class="thumb"><img ng:src="{{phone.imageUrl}}"></a>
 +    <p>{{phone.snippet}}</p>
 +  </li>
 +</ul>
 +</pre>
 +
 +__`app/partials/phone-list.html`:__
 +<pre>
 +TBD: detail view for {{params.phoneId}}
 +</pre>
 +
 +__`app/js/controller.js`:__
 +<pre>
 +/* App Controllers */
 +
 +function PhoneCatCtrl($route) {
 +  var self = this;
 +
 +  $route.when('/phones',
 +              {template: 'partials/phone-list.html',   controller: PhoneListCtrl});
 +  $route.when('/phones/:phoneId',
 +              {template: 'partials/phone-detail.html', controller: PhoneDetailCtrl});
 +  $route.otherwise({redirectTo: '/phones'});
 +
 +  $route.onChange(function(){
 +    self.params = $route.current.params;
 +  });
 +
 +  $route.parent(this);
 +}
 +
 +//PhoneCatCtrl.$inject = ['$route'];
 +
 +
 +function PhoneListCtrl($xhr) {
 +  var self = this;
 +
 +  $xhr('GET', 'phones/phones.json', function(code, response) {
 +    self.phones = response;
 +  });
 +
 +  self.orderProp = 'age';
 +}
 +
 +//PhoneListCtrl.$inject = ['$xhr'];
 +
 +
 +function PhoneDetailCtrl() {}
 +</pre>
 +
 +## Discussion:
 +
 +* __The View.__ Our View template in `index.html` has been reduced down to this:
 +`<ng:view></ng:view>`.  As described above, it is now a "layout template". We added the following
 +two new View templates:
 +
 +    * `app/partials/phone-list.html` for the phone list.  The phone-list view was formerly our
 +    main view. We simply moved the code from `index.html` to here.
 +
 +    * `app/partials/phone-detail.html` for the phone details (just a placeholder template for now).
 +
 +* __The Controller(s).__ We now have a new root controller (`PhoneCatCtrl`) and two
 +sub-controllers (`PhoneListCtrl` and `PhoneDetailCtrl`). These inherit the model properties and
 +behavior from the root controller.
 +
 +    * __`$route.`__ The root controller's job now is to set up the `$route` configuration:
 +
 +        * When the fragment part of the URL in the browser ends in "/phones", `$route` service
 +        grabs the `phone-list.html` template, compiles it, and links it with a new scope that is
 +        controlled by our `PhoneListCtrl` controller.
 +
 +        * When the URL ends in "/phones/:phoneId", `$route` compiles and links the
 +        `phone-detail.html` template as it did with `phone-list.html`. But note the use of the
 +        `:phoneId` parameter declaration in the `path` argument of `$route.when()`: `$route`
 +        services provides all the values for variables defined in this way as
 +        `$route.current.params` map. In our route, `$route.current.params.phoneId` always holds
 +        the current contents of the `:phoneId` portion of the URL. We will use the `phoneId`
 +        parameter when we fetch the phone details in Step 8.
 +
 +        * Any other URL fragment gets redirected to `/phones`.
 +
 +    * __Controller/Scope inheritance.__ In the function passed into `$route`'s `onChange()`
 +    method, we copied url parameters extracted from the current route to the `params` property in
 +    the root scope. This property is inherited by child scopes created for our view controllers
 +    and accessible by these controllers.
 +
 +  * __Tests.__ To automatically verify that everything is wired properly, we write end to end
 +  tests that navigate to various URLs and verify that the correct view was rendered.
 +
 +<table id="tutorial_nav">
 +<tr>
 +<td id="previous_step">{@link tutorial.step_06 Previous}</td>
 +<td id="step_result">{@link  http://angular.github.com/angular-phonecat/step-7/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-6...step-7 Code
 +Diff}</td>
 +<td id="next_step">{@link tutorial.step_08 Next}</td>
 +</tr>
 +</table>
 diff --git a/docs/content/tutorial/step_08.ngdoc b/docs/content/tutorial/step_08.ngdoc new file mode 100755 index 00000000..65ce6883 --- /dev/null +++ b/docs/content/tutorial/step_08.ngdoc @@ -0,0 +1,148 @@ +@workInProgress
 +@ngdoc overview
 +@name Tutorial: Step 8
 +@description
 +<table id="tutorial_nav">
 +<tr>
 +<td id="previous_step">{@link tutorial.step_07 Previous}</td>
 +<td id="step_result">{@link  http://angular.github.com/angular-phonecat/step-8/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-7...step-8 Code
 +Diff}</td>
 +<td id="next_step">{@link tutorial.step_09 Next}</td>
 +</tr>
 +</table>
 +
 +In this step, we implement the Phone Details View template.  Once again we will use {@link
 +angular.services.$xhr $xhr} to fetch our data, and we'll flesh out the `phone-details.html` View
 +template.
 +
 +__`app/partials/phone-details.html`:__
 +<pre>
 +<img ng:src="{{phone.images[0]}}" class="phone"/>
 +
 +<h1>{{phone.name}}</h1>
 +
 +<p>{{phone.description}}</p>
 +
 +<ul class="phone-thumbs">
 +  <li ng:repeat="img in phone.images">
 +    <img ng:src="{{img}}"/>
 +  </li>
 +</ul>
 +
 +<ul class="specs">
 +  <li>
 +    <span>Availability and Networks</span>
 +    <dl>
 +      <dt>Availability</dt>
 +      <dd ng:repeat="availability in phone.availability">{{availability}}</dd>
 +    </dl>
 +  </li>
 +    ...
 +  </li>
 +    <span>Additional Features</span>
 +    <dd>{{phone.additionalFeatures}}</dd>
 +  </li>
 +</ul>
 +</pre>
 +
 +__`app/js/controller.js`:__
 +<pre>
 +function PhoneCatCtrl($route) (same as Step 7)
 +
 +function PhoneListCtrl($xhr) (same as Step 7)
 +
 +function PhoneDetailCtrl($xhr) {
 +  var self = this;
 +
 +  $xhr('GET', 'phones/' + self.params.phoneId + '.json', function(code, response) {
 +    self.phone = response;
 +  });
 +}
 +
 +//PhoneDetailCtrl.$inject = ['$xhr'];
 +</pre>
 +
 +__`app/phones/nexus-s.json`:__ (sample snippet)
 +<pre>
 +{
 +  "additionalFeatures": "Contour Display, Near Field Communications (NFC), Three-axis gyroscope,
 +  Anti-fingerprint display coating, Internet Calling support (VoIP/SIP)",
 +  "android": {
 +      "os": "Android 2.3",
 +      "ui": "Android"
 +  },
 +  ...
 +  "images": [
 +      "img/phones/nexus-s.0.jpg",
 +      "img/phones/nexus-s.1.jpg",
 +      "img/phones/nexus-s.2.jpg",
 +      "img/phones/nexus-s.3.jpg"
 +  ],
 +  "storage": {
 +      "flash": "16384MB",
 +      "ram": "512MB"
 +  }
 +}
 +</pre>
 +
 +__`test/unit/controllerSpec.js`:__
 +<pre>
 +...
 +    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).toBeUndefined();
 +      $browser.xhr.flush();
 +
 +      expect(ctrl.phone).toEqual({name:'phone xyz'});
 +    });
 +...
 +</pre>
 +
 +__`test/e2e/scenarios.js`:__
 +<pre>
 +...
 +  describe('Phone detail view', function() {
 +
 +    beforeEach(function() {
 +      browser().navigateTo('../../app/index.html#/phones/nexus-s');
 +    });
 +
 +
 +    it('should display nexus-s page', function() {
 +      expect(binding('phone.name')).toBe('Nexus S');
 +    });
 +  });
 +...
 +</pre>
 +
 +## Discussion:
 +
 +* Phone Details View Template. There is nothing fancy or new here, just note where we use the
 +angular `{{ expression }}` markup and directives to project phone data from our model into the
 +view.
 +
 +* Note how we used the `$route` `params` object from the scope managed by the root controller
 +(`PhoneCatCtrl`), to construct the path for the phone details xhr request. The rest of this step
 +is simply applying the previously learned concepts and angular APIs to create a large template
 +that displays a lot of data about a phone.
 +
 +* Tests. We updated the existing end to end test and wrote a new unit test that is similar in
 +spirit to the one we wrote for the `PhoneListCtrl` controller.
 +
 +<table id="tutorial_nav">
 +<tr>
 +<td id="previous_step">{@link tutorial.step_07 Previous}</td>
 +<td id="step_result">{@link  http://angular.github.com/angular-phonecat/step-8/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-7...step-8 Code
 +Diff}</td>
 +<td id="next_step">{@link tutorial.step_09 Next}</td>
 +</tr>
 +</table>
 diff --git a/docs/content/tutorial/step_09.ngdoc b/docs/content/tutorial/step_09.ngdoc new file mode 100755 index 00000000..2d6ed925 --- /dev/null +++ b/docs/content/tutorial/step_09.ngdoc @@ -0,0 +1,108 @@ +@workInProgress
 +@ngdoc overview
 +@name Tutorial: Step 9
 +@description
 +<table id="tutorial_nav">
 +<tr>
 +<td id="previous_step">{@link tutorial.step_08 Previous}</td>
 +<td id="step_result">{@link  http://angular.github.com/angular-phonecat/step-9/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-8...step-9 Code
 +Diff}</td>
 +<td id="next_step">{@link tutorial.step_10 Next}</td>
 +</tr>
 +</table>
 +
 +In this step, we have determined that the built-in angular display filters ({@link
 +angular.filter.number number}, {@link angular.filter.currency currency}, {@link
 +angular.filter.date date}, etc.) don't handle what we want to do, so we get to create our own
 +custom {@link angular.filter filter}.
 +
 +In the previous step, the details page displayed either "true" or "false" to indicate whether
 +certain phone features were present or not.  Our custom "checkmark" filter replaces those text
 +strings with glyphs: ✓ for "true", and ✘ for "false".
 +
 +Our filter code lives in `app/js/filters.js`:
 +
 +__`app/index.html`:__
 +<pre>
 +...
 + <script src="lib/angular/angular.js" ng:autobind></script>
 + <script src="js/controllers.js"></script>
 + <script src="app/js/filters.js"></script>
 +...
 +</pre>
 +
 +In the phone details template, we employ our filter for angular expressions whose values are
 +"true" or "false"; `{{ [phone_feature] | checkmark }}`:
 +
 +__`app/partials/phone-detail.html`:__
 +<pre>
 +<img ng:src="{{phone.images[0].large}}" class="phone"/>
 +<h1>{{phone.name}}</h1>
 +<p>{{phone.description}}</p>
 +...
 +<ul class="specs">
 +  ...
 +  <li>
 +    <span>Connectivity</span>
 +    <dl>
 +      <dt>Network Support</dt>
 +      <dd>{{phone.connectivity.cell}}</dd>
 +      <dt>WiFi</dt>
 +      <dd>{{phone.connectivity.wifi}}</dd>
 +      <dt>Bluetooth</dt>
 +      <dd>{{phone.connectivity.bluetooth}}</dd>
 +      <dt>Infrared</dt>
 +      <dd>{{phone.connectivity.infrared | checkmark}}</dd>
 +      <dt>GPS</dt>
 +      <dd>{{phone.connectivity.gps | checkmark}}</dd>
 +    </dl>
 +  </li>
 +...
 +</ul>
 +</pre>
 +
 +__`app/js/filters.js`:__ (New)
 +<pre>
 +angular.filter('checkmark', function(input) {
 +  return input ? '\u2713' : '\u2718';
 +});
 +</pre>
 +
 +__`test/unit/filtersSpec.js`:__ (New)
 +<pre>
 +describe('checkmark filter', function() {
 +
 +  it('should convert boolean values to unicode checkmark or cross', function() {
 +    expect(angular.filter.checkmark(true)).toBe('\u2713');
 +    expect(angular.filter.checkmark(false)).toBe('\u2718');
 +  });
 +})
 +</pre>
 +
 +## Discussion:
 +
 +* This example shows how easy it is to roll your own filters for displaying data.  As explained in
 +the "Writing your own Filters" section of the {@link angular.filter angular.filter} page, you
 +simply register your custom filter function on to the `angular.filter` function.
 +
 +* In this example, our filter name is "checkmark"; our input is either "true" or "false", and we
 +return one of two unicode characters we have chosen to represent true or false (`\u2713` and
 +`\u2718`).
 +
 +* We created a new unit test to verify that our custom filter converts boolean values to unicode
 +characters.
 +
 +<table id="tutorial_nav">
 +<tr>
 +<td id="previous_step">{@link tutorial.step_08 Previous}</td>
 +<td id="step_result">{@link  http://angular.github.com/angular-phonecat/step-9/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-8...step-9 Code
 +Diff}</td>
 +<td id="next_step">{@link tutorial.step_10 Next}</td>
 +</tr>
 +</table>
 diff --git a/docs/content/tutorial/step_10.ngdoc b/docs/content/tutorial/step_10.ngdoc new file mode 100644 index 00000000..130b4023 --- /dev/null +++ b/docs/content/tutorial/step_10.ngdoc @@ -0,0 +1,110 @@ +@workInProgress +@ngdoc overview +@name Tutorial: Step 10 +@description +<table id="tutorial_nav"> +<tr> +<td id="previous_step">{@link tutorial.step_09 Previous}</td> +<td id="step_result">{@link  http://angular.github.com/angular-phonecat/step-10/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-9...step-10 +Code Diff}</td> +<td id="next_step">{@link tutorial.step_11 Next}</td> +</tr> +</table> + +The phone details view displays one large image of the current phone and several smaller thumbnail +images. It would be great if we could replace the large image with any of the thumbnails just by +clicking on the desired thumbnail image. Let's have a look how we can do this with angular. + +__`app/partials/phone-detail.html`:__ +<pre> +<img ng:src="{{mainImageUrl}}" class="phone"/> + +<h1>{{phone.name}}</h1> + +<p>{{phone.description}}</p> + +<ul class="phone-thumbs"> +  <li ng:repeat="img in phone.images"> +    <img ng:src="{{img}}" ng:click="setImage(img)"> +  </li> +</ul> +... +</pre> + +__`app/js/controllers.js`:__ +<pre> +... +function PhoneDetailCtrl($xhr) { +  var self = this; + +  $xhr('GET', 'phones/' + self.params.phoneId + '.json', function(code, response) { +    self.phone = response; +    self.mainImageUrl = response.images[0]; +  }); + +  self.setImage = function(imageUrl) { +    self.mainImageUrl = imageUrl; +  } +} + +//PhoneDetailCtrl.$inject = ['$xhr']; +</pre> + +__`test/e2e/scenarios.js`:__ +<pre> +/* jasmine-like end2end tests go here */ +... +  describe('Phone detail view', function() { + +    beforeEach(function() { +      browser().navigateTo('../../app/index.html#/phones/nexus-s'); +    }); + + +    it('should display nexus-s page', function() { +      expect(binding('phone.name')).toBe('Nexus S'); +    }); + +    it('should display the first phone image as the main phone image', function() { +       expect(element('img.phone').attr('src')).toBe('img/phones/nexus-s.0.jpg'); +    }); + + +    it('should swap main image if a thumbnail image is clicked on', function() { +      element('.phone-thumbs li:nth-child(3) img').click(); +      expect(element('img.phone').attr('src')).toBe('img/phones/nexus-s.2.jpg'); + +      element('.phone-thumbs li:nth-child(1) img').click(); +      expect(element('img.phone').attr('src')).toBe('img/phones/nexus-s.0.jpg'); +    }); +  }); +}); +</pre> + +## Discussion: + +Adding the phone image swapping feature is fairly straightforward: + +* We defined the `mainImageUrl` model property in the details controller (`PhoneDetailCtrl`) and +set the default value of `mainImageUrl` to the first image in the array of images. +* We created a `setImage` controller method to change `mainImageUrl` to the image clicked on by +the user. +* We registered an `{@link angular.directive.ng:click ng:click}` handler for thumb images to use +the `setImage` controller method. +* We expanded the end-to-end test to verify that our new feature is swapping images correctly. + + +<table id="tutorial_nav"> +<tr> +<td id="previous_step">{@link tutorial.step_09 Previous}</td> +<td id="step_result">{@link  http://angular.github.com/angular-phonecat/step-10/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-9...step-10 +Code Diff}</td> +<td id="next_step">{@link tutorial.step_11 Next}</td> +</tr> +</table> diff --git a/docs/content/tutorial/step_11.ngdoc b/docs/content/tutorial/step_11.ngdoc new file mode 100644 index 00000000..e383f406 --- /dev/null +++ b/docs/content/tutorial/step_11.ngdoc @@ -0,0 +1,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>
 | 
