aboutsummaryrefslogtreecommitdiffstats
path: root/docs/content/tutorial
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
parentfdf17d729fa7651e88dc5f27c40b8de875a34a55 (diff)
downloadangular.js-6336b6e89e3a80aec3c4367ab4c2737fd365c030.tar.bz2
chore(docs): restore old tutorial ngdoc files
Diffstat (limited to 'docs/content/tutorial')
-rw-r--r--docs/content/tutorial/index.ngdoc150
-rw-r--r--docs/content/tutorial/step_00.ngdoc216
-rw-r--r--docs/content/tutorial/step_01.ngdoc57
-rw-r--r--docs/content/tutorial/step_02.ngdoc203
-rw-r--r--docs/content/tutorial/step_03.ngdoc181
-rw-r--r--docs/content/tutorial/step_04.ngdoc198
-rw-r--r--docs/content/tutorial/step_05.ngdoc216
-rw-r--r--docs/content/tutorial/step_06.ngdoc105
-rw-r--r--docs/content/tutorial/step_07.ngdoc210
-rw-r--r--docs/content/tutorial/step_08.ngdoc186
-rw-r--r--docs/content/tutorial/step_09.ngdoc121
-rw-r--r--docs/content/tutorial/step_10.ngdoc140
-rw-r--r--docs/content/tutorial/step_11.ngdoc208
-rw-r--r--docs/content/tutorial/the_end.ngdoc21
14 files changed, 2212 insertions, 0 deletions
diff --git a/docs/content/tutorial/index.ngdoc b/docs/content/tutorial/index.ngdoc
new file mode 100644
index 00000000..c26ea2df
--- /dev/null
+++ b/docs/content/tutorial/index.ngdoc
@@ -0,0 +1,150 @@
+@ngdoc overview
+@name Tutorial
+@description
+
+A great way to get introduced to Angular is to work through this tutorial, which walks you through
+the construction of an AngularJS web app. The app you will build is a catalog that displays a list
+of Android devices, lets you filter the list to see only devices that interest you, and then view
+details for any device.
+
+<img src="img/tutorial/catalog_screen.png">
+
+Work through the tutorial to see how Angular makes browsers smarter — without the use of extensions
+or plug-ins. As you work through the tutorial, you will:
+
+* See examples of how to use client-side data binding and dependency injection to build dynamic
+views of data that change immediately in response to user actions.
+* See how Angular creates listeners on your data without the need for DOM manipulation.
+* Learn a better, easier way to test your web apps.
+* Learn how to use Angular services to make common web tasks, such as getting data into your app,
+easier.
+
+And all of this works in any browser without modification to the browser!
+
+When you finish the tutorial you will be able to:
+
+* Create a dynamic application that works in any browser.
+* Define the differences between Angular and common JavaScript frameworks.
+* Understand how data binding works in AngularJS.
+* Use the angular-seed project to quickly boot-strap your own projects.
+* Create and run tests.
+* Identify resources for learning more about AngularJS.
+
+The tutorial guides you through the entire process of building a simple application, including
+writing and running unit and end-to-end tests. Experiments at the end of each step provide
+suggestions for you learn more about AngularJS and the application you are building.
+
+You can go through the whole tutorial in a couple of hours or you may want to spend a pleasant day
+really digging into it. If you're looking for a shorter introduction to AngularJS, check out the
+{@link misc/started Getting Started} document.
+
+
+
+
+
+
+
+# Working with the code
+
+You can follow this tutorial and hack on the code in either the Mac/Linux or the Windows
+environment. Options for working with the tutorial are to use the Git versioning system for source
+code management or to use scripts that copy snapshots of project files into your workspace
+(`sandbox`) directory. Select one of the tabs below and follow the instructions for setting up your
+computer for your preferred option.
+
+<doc:tutorial-instructions show="true">
+ <doc:tutorial-instruction id="git-mac" title="Git on Mac/Linux">
+ <ol>
+ <li><p>Verify that you have <a href="http://java.com/">Java</a> installed by running the
+following command in a terminal window:</p>
+ <pre><code>java -version</code></pre>
+ <p>You will need Java to run unit tests.</p></li>
+ <li><p>Download Git from the <a href="http://git-scm.com/download">Git</a> site.</p>
+ <p>You can build Git from source or use the pre-compiled package.</p></li>
+ <li><p>Clone the angular-phonecat repository located at <a
+href="https://github.com/angular/angular-phonecat">Github</a> by running the following command:</p>
+ <pre><code>git clone git://github.com/angular/angular-phonecat.git</code></pre>
+ <p>This command creates the <code>angular-phonecat</code> directory in your current
+directory.</p></li>
+ <li><p>Change your current directory to <code>angular-phonecat</code>:</p>
+ <pre><code>cd angular-phonecat</code></pre>
+ <p>The tutorial instructions assume you are running all commands from the angular-phonecat
+directory.</p></li>
+ <li><p>You will need an http server running on your system. Mac and Linux machines typically
+have Apache pre-installed, but If you don't already have one installed, you can <a
+href="https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager">install
+node.js</a>. Use <code>node</code> to run <code>scripts/web-server.js</code>, a simple bundled
+http server.</p></li>
+ </ol>
+ </doc:tutorial-instruction>
+
+ <doc:tutorial-instruction id="git-win" title="Git on Windows">
+ <ol>
+ <li><p>You will need Java to run unit tests, so run the following command to verify that you
+have <a href="http://java.com/">Java</a> installed and that the <code>java</code> executable is on
+your <code>PATH</code>.</p>
+ <pre><code>java -version</code></pre>
+ <p></p></li>
+ <li><p>Install msysGit from <a href="http://git-scm.com/download">the Git</a> site.</p></li>
+ <li><p>Open msysGit bash and clone the angular-phonecat repository located at <a
+href="https://github.com/angular/angular-phonecat">Github</a> by running the following command:</p>
+ <pre><code>git clone git://github.com/angular/angular-phonecat.git</code></pre>
+ <p>This command creates the angular-phonecat directory in your current directory.</p></li>
+ <li><p>Change your current directory to angular-phonecat.</p>
+ <pre><code>cd angular-phonecat</code></pre>
+ <p>The tutorial instructions assume you are running all commands from the angular-phonecat
+directory.</p>
+ <p>You should run all <code>git</code> commands from msysGit bash.</p>
+ <p>Other commands like <code>test-server.bat</code> or <code>test.bat</code> should be
+executed from the Windows command line.</li>
+ <li><p>You need an http server running on your system. If you don't already have one
+installed, you can install <a href="http://nodejs.org/">node.js</a>. Download the <a
+href="http://node-js.prcn.co.cc/">pre-compiled binaries</a>, unzip them, and then add
+<code>nodejs\bin</code> into your <code>PATH</code>. Use <code>node</code> to run
+<code>scripts\web-server.js</code>, a simple, bundled http server.</p></li>
+ </ol>
+ </doc:tutorial-instruction>
+
+ <doc:tutorial-instruction id="ss-mac" title="Snapshots on Mac/Linux">
+ <ol>
+ <li><p>You need Java to run unit tests, so verify that you have <a
+href="http://java.com/">Java</a> installed by running the following command in a terminal
+window:</p>
+ <pre><code>java -version</code></pre>
+ <li><p>Download the <a href="http://code.angularjs.org/angular-phonecat/">zip archive</a>
+containing all of the files and unzip them into the [tutorial-dir] directory</p>.</li>
+ <li><p>Change your current directory to [tutorial-dir]/sandbox, as follows:</p>
+ <pre><code>cd [tutorial-dir]/sandbox</code></pre>
+ <p>The tutorial instructions assume you are running all commands from your
+<code>sandbox</code> directory.</p></li>
+ <li><p>You need an http server running on your system and Mac and Linux machines typically
+have Apache pre-installed. If you don't have an http server installed, you can <a
+href="https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager">install
+node.js</a> and use it to run <code>scripts/web-server.js</code>, a simple bundled http
+server.</p></li>
+ </ol>
+ </doc:tutorial-instruction>
+
+ <doc:tutorial-instruction id="ss-win" title="Snapshots on Windows">
+ <ol>
+ <li><p>Verify that you have <a href="http://java.com/">Java</a> installed and that the
+<code>java</code> executable is on your <code>PATH</code> by running the following command in the
+Windows command line:</p>
+ <pre><code>java -version</code></pre>
+ <p>You need Java to run unit tests, so download the <a
+href="http://code.angularjs.org/angular-phonecat/">zip archive</a> that contains all of the files
+and unzip the files into the [tutorial-dir] directory</p></li>
+ <li><p>Change your current directory to [tutorial-dir]/sandbox, as follows:</p>
+ <pre><code>cd [tutorial-dir]/sandbox</code></pre>
+ <p>The tutorial instructions assume you are running all commands from this directory.</p></li>
+ <li><p>You need an http server running on your system, but if you don't already have one
+already installed, you can install <a href="http://nodejs.org/">node.js</a>. Download the <a
+href="http://node-js.prcn.co.cc/">pre-compiled binaries</a>, unzip them, and then add
+<code>nodejs\bin</code> into your <code>PATH</code>. Use <code>node</code> to run
+<code>scripts\web-server.js</code>, a simple bundled http server.</p></li>
+ </ol>
+ </doc:tutorial-instruction>
+</doc:tutorial-instructions>
+
+The last thing to do is to make sure your computer has a web browser and a good text editor
+installed. Now, let's get going with {@link step_00 step 0}.
diff --git a/docs/content/tutorial/step_00.ngdoc b/docs/content/tutorial/step_00.ngdoc
new file mode 100644
index 00000000..b7f469ff
--- /dev/null
+++ b/docs/content/tutorial/step_00.ngdoc
@@ -0,0 +1,216 @@
+@ngdoc overview
+@name Tutorial: 0 - angular-seed
+@description
+
+<ul doc:tutorial-nav="0"></ul>
+
+
+You are now ready to build the Angular phonecat application. In this step, you will become familiar
+with the most important source code files, learn how to start the development servers bundled with
+angular-seed, and run the application in the browser.
+
+
+<doc:tutorial-instructions show="true">
+ <doc:tutorial-instruction id="git-mac" title="Git on Mac/Linux">
+ <ol>
+ <li><p>In angular-phonecat directory, run this command:</p>
+ <pre><code>git checkout -f step-0</code></pre>
+ <p>This resets your workspace to step 0 of the tutorial app.</p>
+ <p>You must repeat this for every future step in the tutorial and change the number to
+ the number of the step you are on. This will cause any changes you made within
+ your working directory to be lost.</p></li>
+
+ <li>To see the app running in a browser, do one of the following:
+ <ul>
+ <li><b>For node.js users:</b>
+ <ol>
+ <li>In a <i>separate</i> terminal tab or window, run
+<code>./scripts/web-server.js</code> to start the web server.</li>
+ <li>Open a browser window for the app and navigate to <a
+href="http://localhost:8000/app/index.html">http://localhost:8000/app/index.html</a></li>
+ </ol>
+ </li>
+ <li><b>For other http servers:</b>
+ <ol>
+ <li>Configure the server to serve the files in the <code>angular-phonecat</code>
+directory.</li>
+ <li>Navigate in your browser to
+<code>http://localhost:[port-number]/[context-path]/app/index.html</code>.</li>
+ </ol>
+ </li>
+ </ul>
+ </li>
+ </ol>
+ </doc:tutorial-instruction>
+
+
+ <doc:tutorial-instruction id="git-win" title="Git on Windows">
+ <ol>
+ <li><p>Open msysGit bash and run this command (in angular-phonecat directory):</p>
+ <pre><code>git checkout -f step-0</code></pre>
+ <p>This resets your workspace to step 0 of the tutorial app.</p>
+ <p>You must repeat this for every future step in the tutorial and change the number to
+ the number of the step you are on. This will cause any changes you made within
+ your working directory to be lost.</p></li>
+ <li>To see the app running in a browser, do one of the following:
+ <ul>
+ <li><b>For node.js users:</b>
+ <ol>
+ <li>In a <i>separate</i> terminal tab or window, run <code>node
+scripts\web-server.js</code> to start the web server.</li>
+ <li>Open a browser window for the app and navigate to <a
+href="http://localhost:8000/app/index.html">http://localhost:8000/app/index.html</a></li>
+ </ol>
+ </li>
+ <li><b>For other http servers:</b>
+ <ol>
+ <li>Configure the server to serve the files in the <code>angular-phonecat</code>
+directory.</li>
+ <li>Navigate in your browser to
+<code>http://localhost:[port-number]/[context-path]/app/index.html</code>.</li>
+ </ol>
+ </li>
+ </ul>
+ </li>
+ </ol>
+ </doc:tutorial-instruction>
+
+
+ <doc:tutorial-instruction id="ss-mac" title="Snapshots on Mac/Linux">
+ <ol>
+ <li><p>In the angular-phonecat directory, run this command:</p>
+ <pre><code>./goto_step.sh 0</code></pre>
+ <p>This resets your workspace to step 0 of the tutorial app.</p>
+ <p>You must repeat this for every future step in the tutorial and change the number to
+ the number of the step you are on. This will cause any changes you made within
+ your working directory to be lost.</p></li>
+ <li>To see the app running in a browser, do one of the following:
+ <ul>
+ <li><b>For node.js users:</b>
+ <ol>
+ <li>In a <i>separate</i> terminal tab or window, run
+<code>./scripts/web-server.js</code> to start the web server.</li>
+ <li>Open a browser window for the app and navigate to <a
+href="http://localhost:8000/app/index.html">http://localhost:8000/app/index.html</a></li>
+ </ol>
+ </li>
+ <li><b>For other http servers:</b>
+ <ol>
+ <li>Configure the server to serve the files in the angular-phonecat
+<code>sandbox</code> directory.</li>
+ <li>Navigate in your browser to
+<code>http://localhost:[port-number]/[context-path]/app/index.html</code>.</li>
+ </ol>
+ </li>
+ </ul>
+ </li>
+ </ol>
+ </doc:tutorial-instruction>
+
+
+ <doc:tutorial-instruction id="ss-win" title="Snapshots on Windows">
+ <ol>
+ <li><p>Open windows command line and run this command (in the angular-phonecat directory):</p>
+ <pre><code>goto_step.bat 0</code></pre>
+ <p>This resets your workspace to step 0 of the tutorial app.</p>
+ <p>You must repeat this for every future step in the tutorial and change the number to
+ the number of the step you are on. This will cause any changes you made within
+ your working directory to be lost.</p></li>
+ <li>To see the app running in a browser, do one of the following:
+ <ul>
+ <li><b>For node.js users:</b>
+ <ol>
+ <li>In a <i>separate</i> terminal tab or window, run <code>node
+scripts\web-server.js</code> to start the web server.</li>
+ <li>Open a browser window for the app and navigate to <a
+href="http://localhost:8000/app/index.html">http://localhost:8000/app/index.html</a></li>
+ </ol>
+ </li>
+ <li><b>For other http servers:</b>
+ <ol>
+ <li>Configure the server to serve the files in the angular-phonecat
+<code>sandbox</code> directory.</li>
+ <li>Navigate in your browser to
+<code>http://localhost:[port-number]/[context-path]/app/index.html</code>.</li>
+ </ol>
+ </li>
+ </ul>
+ </li>
+ </ol>
+ </doc:tutorial-instruction>
+</doc:tutorial-instructions>
+
+
+You can now see the page in your browser. It's not very exciting, but that's OK.
+
+The static HTML page that displays "Nothing here yet!" was constructed with the HTML code shown
+below. The code contains some key Angular elements that we will need going forward.
+
+__`app/index.html`:__
+<pre>
+<!doctype html>
+<html xmlns:ng="http://angularjs.org/" ng:app>
+<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"></script>
+</body>
+</html>
+</pre>
+
+
+
+## What is the code doing?
+
+* xmlns declaration
+
+ <html xmlns:ng="http://angularjs.org" ng:app>
+
+ This `xmlns` declaration for the `ng` namespace must be specified in all Angular applications in
+order to make Angular work with XHTML and IE versions older than 9 (regardless of whether you are
+using XHTML or HTML).
+
+* Angular script tag
+
+ <script src="lib/angular/angular.js">
+
+ This single line of code is all that is needed to bootstrap an angular application.
+
+ The code 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 api/angular.directive.ng:app ng:app} attribute. If Angular finds
+`ng:app`, it creates a root scope for the application and associates it with the element of
+when `ng:app` was declared.
+
+ <img src="img/tutorial/tutorial_00_final.png">
+
+ As you will see shortly, everything in Angular is evaluated within a scope. We'll learn more
+about this in the next steps.
+
+
+## What are all these files in my working directory?
+
+Most of the files in your working directory come from the {@link
+https://github.com/angular/angular-seed angular-seed project} which is typically used to bootstrap
+new Angular projects. The seed project includes the latest Angular libraries, test libraries,
+scripts and a simple example app, all pre-configured for developing a typical web app.
+
+For the purposes of this tutorial, we modified the angular-seed with the following changes:
+
+* Removed the example app
+* Added phone images to `app/img/phones`
+* Added phone data files (JSON) to `app/phones`
+
+
+# Summary
+
+Now let's go to {@link step_01 step 1} and add some content to the web app.
+
+
+<ul doc:tutorial-nav="0"></ul>
diff --git a/docs/content/tutorial/step_01.ngdoc b/docs/content/tutorial/step_01.ngdoc
new file mode 100644
index 00000000..fb8eb26e
--- /dev/null
+++ b/docs/content/tutorial/step_01.ngdoc
@@ -0,0 +1,57 @@
+@ngdoc overview
+@name Tutorial: 1 - Static Template
+@description
+
+<ul doc:tutorial-nav="1"></ul>
+
+
+In order to illustrate how angular enhances standard HTML, you will create a purely *static* HTML
+page and then examine how we can turn this HTML code into a template that angular will use to
+dynamically display the same result with any set of data.
+
+In this step you will add some basic information about two cell phones to an HTML page.
+
+
+<doc:tutorial-instructions step="1" show="true"></doc:tutorial-instructions>
+
+
+The page now contains a list with information about two phones.
+
+The most important changes are listed below. You can see the full diff on {@link
+https://github.com/angular/angular-phonecat/compare/step-0...step-1 GitHub}:
+
+__`app/index.html`:__
+<pre>
+...
+ <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>
+
+
+# Experiments
+
+* Try adding more static HTML to `index.html`. For example:
+
+ <p>Total number of phones: 2</p>
+
+
+# Summary
+
+This addition to your app uses static HTML to display the list. Now, let's go to {@link step_02
+step 2} to learn how to use angular to dynamically generate the same list.
+
+
+<ul doc:tutorial-nav="1"></ul>
diff --git a/docs/content/tutorial/step_02.ngdoc b/docs/content/tutorial/step_02.ngdoc
new file mode 100644
index 00000000..ceeb0e92
--- /dev/null
+++ b/docs/content/tutorial/step_02.ngdoc
@@ -0,0 +1,203 @@
+@ngdoc overview
+@name Tutorial: 2 - Angular Templates
+@description
+
+<ul doc:tutorial-nav="2"></ul>
+
+
+Now it's time to make the web page dynamic -- with Angular. We'll also add a test that verifies the
+code for the controller we are going to add.
+
+There are many ways to structure the code for an application. For Angular apps, we encourage the
+use of {@link http://en.wikipedia.org/wiki/Model–View–Controller the Model-View-Controller (MVC)
+design pattern} to decouple the code and to separate concerns. With that in mind, let's use a
+little Angular and JavaScript to add model, view, and controller components to our app.
+
+
+<doc:tutorial-instructions step="2"></doc:tutorial-instructions>
+
+
+The app now contains a list with three phones.
+
+The most important changes are listed below. You can see the full diff on {@link
+https://github.com/angular/angular-phonecat/compare/step-1...step-2 GitHub}:
+
+
+## Template for the View
+
+The __view__ component is constructed by Angular from this template:
+
+__`app/index.html`:__
+<pre>
+<html ng:app>
+...
+<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"></script>
+ <script src="js/controllers.js"></script>
+</body>
+</html>
+</pre>
+
+We replaced the hard-coded phone list with the {@link api/angular.widget.@ng:repeat ng:repeat
+widget} and two {@link guide/dev_guide.expressions 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. The
+repeater tells Angular to create a `<li>` element for each phone in the list using the first `<li>`
+tag as the template.
+
+ <img src="img/tutorial/tutorial_02_final.png">
+
+* The curly braces around `phone.name` and `phone.snippet` are examples of {@link
+guide/dev_guide.compiler.markup Angular markup}. The curly markup is shorthand for the Angular
+directive {@link api/angular.directive.ng:bind ng:bind}. An `ng:bind` directive indicates a
+template binding point to Angular. Binding points are locations in a 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.
+
+
+## Model and Controller
+
+The data __model__ (a simple array of phones in object literal notation) is instantiated within
+the __controller__ function(`PhoneListCtrl`):
+
+__`app/js/controllers.js`:__
+<pre>
+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>
+
+
+
+
+Although the controller is not yet doing very much controlling, it is playing a crucial role. By
+providing context for our data model, the controller allows us to establish data-binding between
+the model and the view. We connected the dots between the presentation, data, and logic components
+as follows:
+
+* The name of our controller function(in the JavaScript file `controllers.js`) matches the {@link
+api/angular.directive.ng:controller ng:controller} directive in the `<body>` tag (`PhoneListCtrl`).
+* The data is instantiated within the *scope* of our controller function; our template binding
+points are located within the block bounded by the `<body ng:controller="PhoneListCtrl">` tag.
+
+ The concept of a scope in Angular is crucial; a scope can be seen as the glue which allows the
+template, model and controller to work together. Angular uses scopes, along with the information
+contained in the template, data model, and controller, to keep models and views separate, but in
+sync. Any changes made to the model are reflected in the view; any changes that occur in the view
+are reflected in the model.
+
+ To learn more about Angular scopes, see the {@link api/angular.module.ng.$rootScope.Scope angular scope documentation}.
+
+
+## Tests
+
+The "Angular way" makes it easy to test code as it is being developed. Take a look at the following
+unit test for your newly created controller:
+
+__`test/unit/controllersSpec.js`:__
+<pre>
+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>
+
+The test verifies that we have three records in the phones array and the example demonstrates how
+easy it is to create a unit test for code in Angular. Since testing is such a critical part of
+software development, we make it easy to create tests in Angular so that developers are encouraged
+to write them.
+
+Angular developers prefer the syntax of Jasmine's Behavior-driven Development (BDD) framework when
+writing tests. Although Angular does not require you to use Jasmine, we wrote all of the tests in
+this tutorial in Jasmine. 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}.
+
+The angular-seed project is pre-configured to run all unit tests using {@link
+http://code.google.com/p/js-test-driver/ JsTestDriver}. To run the test, do the following:
+
+1. In a _separate_ terminal window or tab, go to the `angular-phonecat` directory and run
+`./scripts/test-server.sh` to start the test web server.
+
+2. Open a new browser tab or window and navigate to {@link http://localhost:9876}.
+
+3. Choose "Capture this browser in strict mode".
+
+ At this point, you can leave this tab open and forget about it. JsTestDriver will use it to
+execute the tests and report the results in the terminal.
+
+4. Execute the test by running `./scripts/test.sh`
+
+ You should see the following or similar output:
+
+ Chrome: Runner reset.
+ .
+ Total 1 tests (Passed: 1; Fails: 0; Errors: 0) (2.00 ms)
+ Chrome 11.0.696.57 Mac OS: Run 1 tests (Passed: 1; Fails: 0; Errors 0) (2.00 ms)
+
+ Yay! The test passed! Or not...
+
+ Note: If you see errors after you run the test, close the browser tab and go back to the terminal
+and kill the script, then repeat the procedure above.
+
+# Experiments
+
+* Add another binding to `index.html`. For example:
+
+ <p>Total number of phones: {{phones.length}}</p>
+
+* Create a new model property in the controller and bind to it from the template. For example:
+
+ this.hello = "Hello, World!"
+
+ Refresh your browser to make sure it says, "Hello, World!"
+
+* Create a repeater that constructs a simple table:
+
+ <table>
+ <tr><th>row number</th></tr>
+ <tr ng:repeat="i in [0, 1, 2, 3, 4, 5, 6, 7]"><td>{{i}}</td></tr>
+ </table>
+
+ Now, make the list 1-based by incrementing `i` by one in the binding:
+
+ <table>
+ <tr><th>row number</th></tr>
+ <tr ng:repeat="i in [0, 1, 2, 3, 4, 5, 6, 7]"><td>{{i+1}}</td></tr>
+ </table>
+
+* Make the unit test fail by changing the `toBe(3)` statement to `toBe(4)`, and rerun the
+`./scripts/test.sh` script.
+
+
+# Summary
+
+You now have a dynamic app that features separate model, view, and controller components, and you
+are testing as you go. Now, let's go to {@link step_03 step 3} to learn how to add full text search
+to the app.
+
+
+<ul doc:tutorial-nav="2"></ul>
diff --git a/docs/content/tutorial/step_03.ngdoc b/docs/content/tutorial/step_03.ngdoc
new file mode 100644
index 00000000..fef4743f
--- /dev/null
+++ b/docs/content/tutorial/step_03.ngdoc
@@ -0,0 +1,181 @@
+@ngdoc overview
+@name Tutorial: 3 - Filtering Repeaters
+@description
+
+<ul doc:tutorial-nav="3"></ul>
+
+
+We did a lot of work in laying a foundation for the app in the last step, so now we'll do something
+simple; we will add full text search (yes, it will be simple!). 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.
+
+
+<doc:tutorial-instructions step="3"></doc:tutorial-instructions>
+
+
+The app now has a search box. Notice that the phone list on the page changes depending on what a
+user types into the search box.
+
+The most important differences between Steps 2 and 3 are listed below. You can see the full diff on
+{@link https://github.com/angular/angular-phonecat/compare/step-2...step-3
+ GitHub}:
+
+
+## Controller
+
+We made no changes to the controller.
+
+
+## Template
+
+__`app/index.html`:__
+<pre>
+...
+ Fulltext Search: <input ng:model="query"/>
+
+ <ul class="phones">
+ <li ng:repeat="phone in phones.$filter(query)">
+ {{phone.name}}
+ <p>{{phone.snippet}}</p>
+ </li>
+ </ul>
+...
+</pre>
+
+We added a standard HTML `<input>` tag and used angular's {@link api/angular.module.ng.$filter.filter $filter}
+function to process the input for the `ng:repeater`.
+
+This lets a user enter search criteria and immediately see the effects of their search on the phone
+list. This new code demonstrates the following:
+
+* 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 code, the data that a user types into the input box (named __`query`__) is immediately
+available as a filter input in the list repeater (`phone in phones.$filter(`__`query`__`)`). When
+changes to the data model cause the repeater's input to change, the repeater efficiently updates
+the DOM to reflect the current state of the model.
+
+ <img src="img/tutorial/tutorial_03_final.png">
+
+* Use of `$filter`. The {@link api/angular.module.ng.$filter.filter $filter} method uses the `query` value to
+create a new array that contains only those records that match the `query`.
+
+ `ng:repeat` automatically updates the view in response to the changing number of phones returned
+by the `$filter`. The process is completely transparent to the developer.
+
+## Test
+
+In Step 2, we learned how to write and run unit tests. Unit tests are perfect for testing
+controllers and other components of our application written in JavaScript, but they can't easily
+test DOM manipulation or the wiring of our application. For these, an end-to-end test is a much
+better choice.
+
+The search feature was fully implemented via templates and data-binding, so we'll write our first
+end-to-end test, to verify that the feature works.
+
+__`test/e2e/scenarios.js`:__
+<pre>
+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>
+
+Even though the syntax of this test looks very much like our controller unit test written with
+Jasmine, the end-to-end test uses APIs of {@link guide/dev_guide.e2e-testing Angular's end-to-end
+test runner}.
+
+To run the end-to-end test, open one of the following in a new browser tab:
+
+* node.js users: {@link http://localhost:8000/test/e2e/runner.html}
+* users with other http servers:
+`http://localhost:[port-number]/[context-path]/test/e2e/runner.html`
+* casual reader: {@link http://angular.github.com/angular-phonecat/step-3/test/e2e/runner.html}
+
+This test verifies that the search box and the repeater are correctly wired together. Notice how
+easy it is to write end-to-end tests in Angular. Although this example is for a simple test, it
+really is that easy to set up any functional, readable, end-to-end test.
+
+# Experiments
+
+* Display the current value of the `query` model by adding a `{{query}}` binding into the
+`index.html` template, and see how it changes when you type in the input box.
+
+* Let's see how we can get the current value of the `query` model to appear in the HTML page title.
+
+ You might think you could just add the {{query}} to the title tag element as follows:
+
+ <title>Google Phone Gallery: {{query}}</title>
+
+ However, when you reload the page, you won't see the expected result. This is because the "query"
+model lives in the scope defined by the body element:
+
+ <body ng:controller="PhoneListCtrl">
+
+ If you want to bind to the query model from the `<title>` element, you must __move__ the
+`ng:controller` declaration to the HTML element because it is the common parent of both the body
+and title elements:
+
+ <html ng:controller="PhoneListCtrl">
+
+ Be sure to *remove* the `ng:controller` declaration from the body element.
+
+ While using double curlies works fine in within the title element, you might have noticed that
+for a split second they are actually displayed to the user while the page is loading. A better
+solution would be to use the {@link api/angular.directive.ng:bind ng:bind} or {@link
+api/angular.directive.ng:bind-template ng:bind-template} directives, which are invisible to the
+user while the page is loading:
+
+ <title ng:bind-template="Google Phone Gallery: {{query}}">Google Phone Gallery</title>
+
+* Add the following end-to-end test into the `describe` block within `test/e2e/scenarios.js`:
+
+ <pre>
+ it('should display the current filter value within an element with id "status"',
+ function() {
+ expect(element('#status').text()).toMatch(/Current filter: \s*$/);
+
+ input('query').enter('nexus');
+
+ expect(element('#status').text()).toMatch(/Current filter: nexus\s*$/);
+
+ //alternative version of the last assertion that tests just the value of the binding
+ using('#status').expect(binding('query')).toBe('nexus');
+ });
+ </pre>
+
+ Refresh the browser tab with the end-to-end test runner to see the test fail. To make the test
+pass, edit the `index.html` template to add a `div` or `p` element with `id` `"status"` and content
+with the `query` binding.
+
+* Add a `pause()` statement into an end-to-end test and rerun it. You'll see the runner pause; this
+gives you the opportunity to explore the state of your application while it is displayed in the
+browser. The app is live! You can change the search query to prove it. Notice how useful this is
+for troubleshooting end-to-end tests.
+
+
+# Summary
+
+We have now added full text search and included a test to verify that search works! Now let's go on
+to {@link step_04 step 4} to learn how to add sorting capability to the phone app.
+
+
+<ul doc:tutorial-nav="3"></ul>
+
diff --git a/docs/content/tutorial/step_04.ngdoc b/docs/content/tutorial/step_04.ngdoc
new file mode 100644
index 00000000..a5fefd74
--- /dev/null
+++ b/docs/content/tutorial/step_04.ngdoc
@@ -0,0 +1,198 @@
+@ngdoc overview
+@name Tutorial: 4 - Two-way Data Binding
+@description
+
+<ul doc:tutorial-nav="4"></ul>
+
+
+In this step, you will add a feature to let your users control the order of the items in the phone
+list. The dynamic ordering is implemented by creating a new model property, wiring it together with
+the repeater, and letting the data binding magic do the rest of the work.
+
+
+<doc:tutorial-instructions step="4"></doc:tutorial-instructions>
+
+
+You should see that in addition to the search box, the app displays a drop down menu that allows
+users to control the order in which the phones are listed.
+
+The most important differences between Steps 3 and 4 are listed below. You can see the full diff on
+{@link https://github.com/angular/angular-phonecat/compare/step-3...step-4 GitHub}:
+
+
+## Template
+
+__`app/index.html`:__
+<pre>
+...
+ <ul class="controls">
+ <li>
+ Search: <input type="text" ng:model="query"/>
+ </li>
+ <li>
+ Sort by:
+ <select ng:model="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>
+
+We made the following changes to the `index.html` template:
+
+* First, we added a `<select>` html element named `orderProp`, so that our users can pick from the
+two provided sorting options.
+
+ <img src="img/tutorial/tutorial_04-06_final.png">
+
+* We then chained the `$filter` method with {@link api/angular.module.ng.$filter.orderBy `$orderBy`} method to
+further process the input into the repeater. `$orderBy` is a utility method similar to {@link
+api/angular.module.ng.$filter.filter `$filter`}, but instead of filtering an array, it reorders it.
+
+Angular creates a two way data-binding between the select element and the `orderProp` model.
+`orderProp` is then used as the input for the `$orderBy` method.
+
+As we discussed in the section about data-binding and the repeater in step 3, whenever the model
+changes (for example because a user changes the order with the select drop down menu), Angular's
+data-binding will cause the view to automatically update. No bloated DOM manipulation code is
+necessary!
+
+
+
+## Controller
+
+__`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>
+
+* We modified the `phones` model - the array of phones - and added an `age` property to each phone
+record. This property is used to order phones by age.
+
+* We added a line to the controller that sets the default value of `orderProp` to `age`. If we had
+not set the default value here, angular would have used the value of the first `<option>` element
+(`'name'`) when it initialized the data model.
+
+ This is a good time to talk about two-way data-binding. Notice that when the app is loaded in the
+browser, "Newest" is selected in the drop down menu. This is because we set `orderProp` to `'age'`
+in the controller. So the binding works in the direction from our model to the UI. Now if you
+select "Alphabetically" in the drop down menu, the model will be updated as well and the phones
+will be reordered. That is the data-binding doing its job in the opposite direction — from the UI
+to the model.
+
+
+
+## Test
+
+The changes we made should be verified with both a unit test and an end-to-end test. Let's look at
+the unit test first.
+
+__`test/unit/controllerSpec.js`:__
+<pre>
+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>
+
+
+The unit test now verifies that the default ordering property is set.
+
+We used Jasmine's API to extract the controller construction into a `beforeEach` block, which is
+shared by all tests in the parent `describe` block.
+
+To run the unit tests, once again 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)
+
+
+Let's turn our attention to the end-to-end test.
+
+__`test/e2e/scenarios.js`:__
+<pre>
+...
+ it('should be possible to control phone order via the drop down select box',
+ function() {
+
+ // narrow the dataset to make the test assertions shorter
+ input('query').enter('tablet');
+
+ 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>
+
+The end-to-end test verifies that the ordering mechanism of the select box is working correctly.
+
+You can now refresh the browser tab with the end-to-end test runner to see the tests run, or you
+can see them running on {@link
+http://angular.github.com/angular-phonecat/step-4/test/e2e/runner.html
+Angular's server}.
+
+# Experiments
+
+* In the `PhoneListCtrl` controller, remove the statement that sets the `orderProp` value and
+you'll see that the ordering as well as the current selection in the dropdown menu will default to
+"Alphabetical".
+
+* Add an `{{orderProp}}` binding into the `index.html` template to display its current value as
+text.
+
+# Summary
+
+Now that you have added list sorting and tested the app, go to {@link step_05 step 5} to learn
+about Angular services and how Angular uses dependency injection.
+
+
+<ul doc:tutorial-nav="4"></ul>
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>
diff --git a/docs/content/tutorial/step_06.ngdoc b/docs/content/tutorial/step_06.ngdoc
new file mode 100644
index 00000000..45e667de
--- /dev/null
+++ b/docs/content/tutorial/step_06.ngdoc
@@ -0,0 +1,105 @@
+@ngdoc overview
+@name Tutorial: 6 - Templating Links & Images
+@description
+
+<ul doc:tutorial-nav="6"></ul>
+
+
+In this step, you will add thumbnail images for the phones in the phone list, and links that, for
+now, will go nowhere. In subsequent steps you will use the links to display additional information
+about the phones in the catalog.
+
+
+<doc:tutorial-instructions step="6"></doc:tutorial-instructions>
+
+
+You should now see links and images of the phones in the list.
+
+The most important changes are listed below. You can see the full diff on {@link
+https://github.com/angular/angular-phonecat/compare/step-5...step-6
+GitHub}:
+
+
+## Data
+
+Note that the `phones.json` file contains unique ids and image urls for each of the phones. The
+urls point to the `app/img/phones/` directory.
+
+__`app/phones/phones.json`__ (sample snippet):
+<pre>
+ [
+ {
+ ...
+ "id": "motorola-defy-with-motoblur",
+ "imageUrl": "img/phones/motorola-defy-with-motoblur.0.jpg",
+ "name": "Motorola DEFY\u2122 with MOTOBLUR\u2122",
+ ...
+ },
+ ...
+ ]
+</pre>
+
+
+## Template
+
+__`app/index.html`:__
+<pre>
+...
+ <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>
+
+To dynamically generate links that will in the future lead to phone detail pages, we used the
+now-familiar {@link guide/dev_guide.compiler.markup double-curly brace markup} in the `href`
+attribute values. In step 2, we added the `{{phone.name}}` binding as the element content. In this
+step the `{{phone.id}}` binding is used in the element attribute.
+
+We also added phone images next to each record using an image tag with the {@link
+api/angular.directive.ng:src ng:src} directive. That directive prevents the browser from treating
+the angular `{{ expression }}` markup literally, which it would have done if we had only specified
+an attribute binding in a regular `src` attribute (`<img src="{{phone.imageUrl}}">`). Using
+`ng:src` prevents the browser from making an http request to an invalid location.
+
+
+## Test
+
+__`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>
+
+We added a new end-to-end test to verify that the app is generating correct links to the phone
+views that we will implement in the upcoming steps.
+
+You can now refresh the browser tab with the end-to-end test runner to see the tests run, or you
+can see them running on {@link
+http://angular.github.com/angular-phonecat/step-6/test/e2e/runner.html
+angular's server}.
+
+# Experiments
+
+* Replace the `ng:src` directive with a plain old `<src>` attribute. Using tools such as Firebug,
+or Chrome's Web Inspector, or inspecting the webserver access logs, confirm that the app is indeed
+making an extraneous request to `/app/%7B%7Bphone.imageUrl%7D%7D` (or
+`/app/index.html/{{phone.imageUrl}}`).
+
+
+# Summary
+
+Now that you have added phone images and links, go to {@link step_07 step 7} to learn about angular
+layout templates and how angular makes it easy to create applications that have multiple views.
+
+
+<ul doc:tutorial-nav="6"></ul>
diff --git a/docs/content/tutorial/step_07.ngdoc b/docs/content/tutorial/step_07.ngdoc
new file mode 100644
index 00000000..0aebb9fe
--- /dev/null
+++ b/docs/content/tutorial/step_07.ngdoc
@@ -0,0 +1,210 @@
+@ngdoc overview
+@name Tutorial: 7 - Routing & Multiple Views
+@description
+
+<ul doc:tutorial-nav="7"></ul>
+
+
+In this step, you will learn how to create a layout template and how to build an app that has
+multiple views by adding routing.
+
+
+<doc:tutorial-instructions step="7"></doc:tutorial-instructions>
+
+
+Note that you are redirected to `app/index.html#/phones` and the same phone list appears in the
+browser. When you click on a phone link the stub of a phone detail page is displayed.
+
+
+The most important changes are listed below. You can see the full diff on {@link
+https://github.com/angular/angular-phonecat/compare/step-6...step-7
+GitHub}:
+
+
+## Multiple Views, Routing and Layout Template
+
+Our app is slowly growing and becoming more complex. Before step 7, the app provided our users with
+a single view (the list of all phones), and all of the template code was located in the
+`index.html` file. The next step in building the app is to add 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.
+
+Application routes in angular are declared via the {@link api/angular.module.ng.$route $route}
+service. This service 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 (back and forward navigation) and bookmarks.
+
+
+## Controllers
+
+__`app/js/controller.js`:__
+<pre>
+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'];
+...
+</pre>
+
+We created a new controller called `PhoneCatCtrl`. We declared its dependency on the `$route`
+service and used this service to declare that our application consists of two different views:
+
+* The phone list view will be shown when the URL hash fragment is `/phones`. To construct this
+view, angular will use the `phone-list.html` template and the `PhoneListCtrl` controller.
+
+* The phone details view will be shown when the URL hash fragment matches '/phone/:phoneId', where
+`:phoneId` is a variable part of the URL. To construct the phone details view, angular will use the
+`phone-detail.html` template and the `PhoneDetailCtrl` controller.
+
+We reused the `PhoneListCtrl` controller that we constructed in previous steps and we added a new,
+empty `PhoneDetailCtrl` controller to the `app/js/controllers.js` file for the phone details view.
+
+The statement `$route.otherwise({redirectTo: '/phones'})` triggers a redirection to `/phones` when
+the browser address doesn't match either of our routes.
+
+Thanks to the `$route.parent(this);` statement and `ng:controller="PhoneCatCtrl"` declaration in
+the `index.html` template, the `PhoneCatCtrl` controller has a special role in our app. It is the
+"root" controller and the parent controller for the other two sub-controllers (`PhoneListCtrl` and
+`PhoneDetailCtrl`). The sub-controllers inherit the model properties and behavior from the root
+controller.
+
+Note the use of the `:phoneId` parameter in the second route declaration. The `$route` service uses
+the route declaration — `'/phones/:phoneId'` — as a template that is matched against the current
+URL. All variables defined with the `:` notation are extracted into the `$route.current.params` map.
+
+The `params` alias created in the {@link api/angular.module.ng.$route `$route.onChange`} callback
+allows us to use the `phoneId` property of this map in the `phone-details.html` template.
+
+
+## Template
+
+The `$route` service is usually used in conjunction with the {@link api/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.
+
+__`app/index.html`:__
+<pre>
+<html ng:app>
+...
+<body ng:controller="PhoneCatCtrl">
+
+ <ng:view></ng:view>
+
+ <script src="lib/angular/angular.js"></script>
+ <script src="js/controllers.js"></script>
+</body>
+</html>
+</pre>
+
+Note that we removed most of the code in the `index.html` template and replaced it with a single
+line containing the `ng:view` tag. The code that we removed was placed into the `phone-list.html`
+template:
+
+__`app/partials/phone-list.html`:__
+<pre>
+<ul class="predicates">
+ <li>
+ Search: <input type="text" ng:model="query"/>
+ </li>
+ <li>
+ Sort by:
+ <select ng:model="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>
+
+<img src="img/tutorial/tutorial_07_final.png">
+
+We also added a placeholder template for the phone details view:
+
+__`app/partials/phone-detail.html`:__
+<pre>
+TBD: detail view for {{params.phoneId}}
+</pre>
+
+Note how we are using `params` model defined in the `PhoneCatCtrl` controller.
+
+
+## Test
+
+To automatically verify that everything is wired properly, we wrote end-to-end tests that navigate
+to various URLs and verify that the correct view was rendered.
+
+<pre>
+...
+ it('should redirect index.html to index.html#/phones', function() {
+ browser().navigateTo('../../app/index.html');
+ expect(browser().location().hash()).toBe('/phones');
+ });
+...
+
+ describe('Phone detail view', function() {
+
+ beforeEach(function() {
+ browser().navigateTo('../../app/index.html#/phones/nexus-s');
+ });
+
+
+ it('should display placeholder page with phoneId', function() {
+ expect(binding('params.phoneId')).toBe('nexus-s');
+ });
+ });
+</pre>
+
+
+You can now refresh the browser tab with the end-to-end test runner to see the tests run, or you
+can see them running on {@link
+http://angular.github.com/angular-phonecat/step-7/test/e2e/runner.html
+angular's server}.
+
+
+# Experiments
+
+* Try to add an `{{orderProp}}` binding to `index.html`, and you'll see that nothing happens even
+when you are in the phone list view. This is because the `orderProp` model is visible only in the
+scope managed by `PhoneListCtrl`, which is associated with the `<ng:view>` element. If you add the
+same binding into the `phone-list.html` template, the binding will work as expected.
+
+* In `PhoneCatCtrl`, create a new model called "`hero`" with `this.hero = 'Zoro'`. In
+`PhoneListCtrl` let's shadow it with `this.hero = 'Batman'`, and in `PhoneDetailCtrl` we'll use
+`this.hero = "Captain Proton"`. Then add the `<p>hero = {{hero}}</p>` to all three of our templates
+(`index.html`, `phone-list.html`, and `phone-detail.html`). Open the app and you'll see scope
+inheritance and model property shadowing do some wonders.
+
+# Summary
+
+With the routing set up and the phone list view implemented, we're ready to go to {@link step_08
+step 8} to implement the phone details view.
+
+
+<ul doc:tutorial-nav="7"></ul>
diff --git a/docs/content/tutorial/step_08.ngdoc b/docs/content/tutorial/step_08.ngdoc
new file mode 100644
index 00000000..a81c689b
--- /dev/null
+++ b/docs/content/tutorial/step_08.ngdoc
@@ -0,0 +1,186 @@
+@ngdoc overview
+@name Tutorial: 8 - More Templating
+@description
+
+<ul doc:tutorial-nav="8"></ul>
+
+
+In this step, you will implement the phone details view, which is displayed when a user clicks on a
+phone in the phone list.
+
+
+<doc:tutorial-instructions step="8"></doc:tutorial-instructions>
+
+
+Now when you click on a phone on the list, the phone details page with phone-specific information
+is displayed.
+
+To implement the phone details view we will use {@link api/angular.module.ng.$xhr $xhr} to fetch our
+data, and we'll flesh out the `phone-details.html` view template.
+
+The most important changes are listed below. You can see the full diff on {@link
+https://github.com/angular/angular-phonecat/compare/step-7...step-8
+GitHub}:
+
+## Data
+
+In addition to `phones.json`, the `app/phones/` directory also contains one json file for each
+phone:
+
+__`app/phones/nexus-s.json`:__ (sample snippet)
+<pre>
+{
+ "additionalFeatures": "Contour Display, Near Field Communications (NFC),...",
+ "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>
+
+
+Each of these files describes various properties of the phone using the same data structure. We'll
+show this data in the phone detail view.
+
+
+## Controller
+
+We'll expand the `PhoneDetailCtrl` by using the `$xhr` service to fetch the json files. This works
+the same way as the phone list controller.
+
+__`app/js/controller.js`:__
+<pre>
+function PhoneDetailCtrl($xhr) {
+ var self = this;
+
+ $xhr('GET', 'phones/' + self.params.phoneId + '.json', function(code, response) {
+ self.phone = response;
+ });
+}
+
+//PhoneDetailCtrl.$inject = ['$xhr'];
+</pre>
+
+To construct the URL for the HTTP request, we use `params.phoneId` extracted from the current route
+in the `PhoneCatCtrl` controller.
+
+
+## Template
+
+The TBD placeholder line has been replaced with lists and bindings that comprise the phone details.
+Note where we use the angular `{{expression}}` markup and `ng:repeater`s to project phone data from
+our model into the view.
+
+
+__`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>
+
+<img src="img/tutorial/tutorial_08-09_final.png">
+
+## Test
+
+We wrote a new unit test that is similar to the one we wrote for the `PhoneListCtrl` controller in
+step 5.
+
+__`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>
+
+To run the unit tests, execute the `./scripts/test.sh` script and you should see the following
+output.
+
+ Chrome: Runner reset.
+ ...
+ Total 3 tests (Passed: 3; Fails: 0; Errors: 0) (5.00 ms)
+ Chrome 11.0.696.57 Mac OS: Run 3 tests (Passed: 3; Fails: 0; Errors 0) (5.00 ms)
+
+
+We also added a new end-to-end test that navigates to the Nexus S detail page and verifies that the
+heading on the page is "Nexus S".
+
+__`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>
+
+
+You can now refresh the browser tab with the end-to-end test runner to see the tests run, or you
+can see them running on {@link
+http://angular.github.com/angular-phonecat/step-8/test/e2e/runner.html
+angular's server}.
+
+# Experiments
+
+* Using the {@link guide/dev_guide.e2e-testing Angular's end-to-end test runner API}, write a test
+that verifies that we display 4 thumbnail images on the Nexus S details page.
+
+
+# Summary
+
+Now that the phone details view is in place, proceed to {@link step_09 step 9} to learn how to
+write your own custom display filter.
+
+
+<ul doc:tutorial-nav="8"></ul>
diff --git a/docs/content/tutorial/step_09.ngdoc b/docs/content/tutorial/step_09.ngdoc
new file mode 100644
index 00000000..c0df9e1f
--- /dev/null
+++ b/docs/content/tutorial/step_09.ngdoc
@@ -0,0 +1,121 @@
+@ngdoc overview
+@name Tutorial: 9 - Filters
+@description
+
+<ul doc:tutorial-nav="9"></ul>
+
+
+In this step you will learn how to create your own custom display filter.
+
+
+<doc:tutorial-instructions step="9"></doc:tutorial-instructions>
+
+
+Navigate to one of the detail pages.
+
+In the previous step, the details page displayed either "true" or "false" to indicate whether
+certain phone features were present or not. We have used a custom filter to convert those text
+strings into glyphs: ✓ for "true", and ✘ for "false". Let's see, what the filter code looks like.
+
+The most important changes are listed below. You can see the full diff on {@link
+https://github.com/angular/angular-phonecat/compare/step-8...step-9
+GitHub}:
+
+
+## Custom Filter
+
+In order to create a new filter, simply register your custom filter function with the {@link
+api/angular.module.ng.$filter `angular.module.ng.$filter`} API.
+
+__`app/js/filters.js`:__
+<pre>
+angular.module.ng.$filter('checkmark', function(input) {
+ return input ? '\u2713' : '\u2718';
+});
+</pre>
+
+The name of our filter is "checkmark". The `input` evaluates to either `true` or `false`, and we
+return one of two unicode characters we have chosen to represent true or false (`\u2713` and
+`\u2718`).
+
+
+## Template
+
+Since the filter code lives in the `app/js/filters.js` file, we need to include this file in our
+layout template.
+
+__`app/index.html`:__
+<pre>
+...
+ <script src="js/controllers.js"></script>
+ <script src="js/filters.js"></script>
+...
+</pre>
+
+The syntax for using filters in angular templates is as follows:
+
+ {{ expression | filter }}
+
+Let's employ the filter in the phone details template:
+
+
+
+__`app/partials/phone-detail.html`:__
+<pre>
+...
+ <dl>
+ <dt>Infrared</dt>
+ <dd>{{phone.connectivity.infrared | checkmark}}</dd>
+ <dt>GPS</dt>
+ <dd>{{phone.connectivity.gps | checkmark}}</dd>
+ </dl>
+...
+</pre>
+
+
+## Test
+
+Filters, like any other component, should be tested and these tests are very easy to write.
+
+__`test/unit/filtersSpec.js`:__
+<pre>
+describe('checkmark filter', function() {
+
+ it('should convert boolean values to unicode checkmark or cross', function() {
+ expect(angular.module.ng.$filter.checkmark(true)).toBe('\u2713');
+ expect(angular.module.ng.$filter.checkmark(false)).toBe('\u2718');
+ });
+})
+</pre>
+
+To run the unit tests, execute the `./scripts/test.sh` script and you should see the following
+output.
+
+ Chrome: Runner reset.
+ ....
+ Total 4 tests (Passed: 4; Fails: 0; Errors: 0) (3.00 ms)
+ Chrome 11.0.696.57 Mac OS: Run 4 tests (Passed: 4; Fails: 0; Errors 0) (3.00 ms)
+
+
+# Experiments
+
+* Let's experiment with some of the {@link api/angular.module.ng.$filter built-in angular filters} and add the
+following bindings to `index.html`:
+ * `{{ "lower cap string" | uppercase }}`
+ * `{{ {foo: "bar", baz: 23} | json }}`
+ * `{{ 1304375948024 | date }}`
+ * `{{ 1304375948024 | date:"MM/dd/yyyy @ h:mma" }}`
+
+* We can also create a model with an input element, and combine it with a filtered binding. Add
+the following to index.html:
+
+ <input ng:model="userInput"> Uppercased: {{ userInput | uppercase }}
+
+
+# Summary
+
+Now that you have learned how to write and test a custom filter, go to {@link step_10 step 10} to
+learn how we can use angular to enhance the phone details page further.
+
+
+<ul doc:tutorial-nav="9"></ul>
diff --git a/docs/content/tutorial/step_10.ngdoc b/docs/content/tutorial/step_10.ngdoc
new file mode 100644
index 00000000..73e8b354
--- /dev/null
+++ b/docs/content/tutorial/step_10.ngdoc
@@ -0,0 +1,140 @@
+@ngdoc overview
+@name Tutorial: 10 - Event Handlers
+@description
+
+<ul doc:tutorial-nav="10"></ul>
+
+
+In this step, you will add a clickable phone image swapper to the phone details page.
+
+
+<doc:tutorial-instructions step="10"></doc:tutorial-instructions>
+
+
+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 at how we can do this with angular.
+
+The most important changes are listed below. You can see the full diff on {@link
+https://github.com/angular/angular-phonecat/compare/step-9...step-10
+GitHub}:
+
+
+## Controller
+
+__`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>
+
+In the `PhoneDetailCtrl` controller, we created the `mainImageUrl` model property and set its
+default value to the first phone image url.
+
+We also created a `setImage` controller method to change the value of `mainImageUrl`.
+
+
+## Template
+
+__`app/partials/phone-detail.html`:__
+<pre>
+<img ng:src="{{mainImageUrl}}" class="phone"/>
+
+...
+
+<ul class="phone-thumbs">
+ <li ng:repeat="img in phone.images">
+ <img ng:src="{{img}}" ng:click="setImage(img)">
+ </li>
+</ul>
+...
+</pre>
+
+We bound the `ng:src` attribute of the large image to the `mainImageUrl` property.
+
+We also registered an {@link api/angular.directive.ng:click `ng:click`} handler with thumbnail
+images. When a user clicks on one of the thumbnail images, the handler will use the `setImage`
+controller method to change the value of the `mainImageUrl` property to the url of the thumbnail
+image.
+
+<img src="img/tutorial/tutorial_10-11_final.png">
+
+## Test
+
+To verify this new feature, we added two end-to-end tests. One verifies that the main image is set
+to the first phone image by default. The second test clicks on several thumbnail images and
+verifies that the main image changed appropriately.
+
+__`test/e2e/scenarios.js`:__
+<pre>
+...
+ describe('Phone detail view', function() {
+
+ beforeEach(function() {
+ browser().navigateTo('../../app/index.html#/phones/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>
+
+You can now refresh the browser tab with the end-to-end test runner to see the tests run, or you
+can see them running on {@link
+http://angular.github.com/angular-phonecat/step-8/test/e2e/runner.html
+angular's server}.
+
+# Experiments
+
+* Let's add a new controller method to `PhoneCatCtrl`:
+
+ this.hello = function(name) {
+ alert('Hello ' + (name || 'world') + '!');
+ }
+
+ and add:
+
+ <button ng:click="hello('Elmo')">Hello</button>
+
+ to the `index.html` template.
+
+ The controller methods are inherited between controllers/scopes, so you can use the same snippet
+in the `phone-list.html` template as well.
+
+* Move the `hello` method from `PhoneCatCtrl` to `PhoneListCtrl` and you'll see that the button
+declared in `index.html` will stop working, while the one declared in the `phone-list.html`
+template remains operational.
+
+
+# Summary
+
+With the phone image swapper in place, we're ready for {@link step_11 step 11} (the last step!) to
+learn an even better way to fetch data.
+
+
+<ul doc:tutorial-nav="10"></ul>
diff --git a/docs/content/tutorial/step_11.ngdoc b/docs/content/tutorial/step_11.ngdoc
new file mode 100644
index 00000000..c6b70065
--- /dev/null
+++ b/docs/content/tutorial/step_11.ngdoc
@@ -0,0 +1,208 @@
+@ngdoc overview
+@name Tutorial: 11 - REST and Custom Services
+@description
+
+<ul doc:tutorial-nav="11"></ul>
+
+
+In this step, you will improve the way our app fetches data.
+
+
+<doc:tutorial-instructions step="11"></doc:tutorial-instructions>
+
+
+The last improvement we will make to our app is to 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
+api/angular.module.ng.$xhr $xhr} API, HTTP methods and URLs.
+
+The most important changes are listed below. You can see the full diff on {@link
+https://github.com/angular/angular-phonecat/compare/step-10...step-11
+GitHub}:
+
+
+## Template
+
+The custom service is defined in `app/js/services.js` so we need to include this file in our layout
+template:
+
+__`app/index.html`.__
+<pre>
+...
+ <script src="js/services.js"></script>
+...
+</pre>
+
+## Service
+
+__`app/js/services.js`.__
+<pre>
+ angular.module.ng('Phone', function($resource) {
+ return $resource('phones/:phoneId.json', {}, {
+ query: {method: 'GET', params: {phoneId: 'phones'}, isArray: true}
+ });
+ });
+</pre>
+
+We used the {@link api/angular.module.ng} API to register a custom service. We passed in the name of
+the service - 'Phone' - and a factory function. The factory function is similar to a controller's
+constructor in that both can declare dependencies via function arguments. The Phone service
+declared a dependency on the `$resource` service.
+
+The {@link api/angular.module.ng.$resource `$resource`} service makes it easy to create a {@link
+http://en.wikipedia.org/wiki/Representational_State_Transfer RESTful} client with just a few lines
+of code. This client can then be used in our application, instead of the lower-level {@link
+api/angular.module.ng.$xhr $xhr} service.
+
+
+## Controller
+
+We simplified our sub-controllers (`PhoneListCtrl` and `PhoneDetailCtrl`) by factoring out the
+lower-level {@link api/angular.module.ng.$xhr $xhr} service, replacing it with a new service called
+`Phone`. Angular's {@link api/angular.module.ng.$resource `$resource`} service is easier to use than
+{@link api/angular.module.ng.$xhr $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.
+
+__`app/js/controllers.js`.__
+<pre>
+...
+
+function PhoneListCtrl(Phone) {
+ this.orderProp = 'age';
+ this.phones = Phone.query();
+}
+//PhoneListCtrl.$inject = ['Phone'];
+
+
+function PhoneDetailCtrl(Phone) {
+ var self = this;
+
+ self.phone = Phone.get({phoneId: self.params.phoneId}, function(phone) {
+ self.mainImageUrl = phone.images[0];
+ });
+
+ ...
+}
+//PhoneDetailCtrl.$inject = ['Phone'];
+</pre>
+
+Notice how in `PhoneListCtrl` we replaced:
+
+ $xhr('GET', 'phones/phones.json', function(code, response) {
+ self.phones = response;
+ });
+
+with:
+
+ this.phones = Phone.query();
+
+This is a simple statement that we want to query for all phones.
+
+An important thing to notice in the code above is that we don't pass any callback functions when
+invoking methods of our Phone service. Although 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.
+
+Sometimes, relying on the future object and data-binding alone is not sufficient to do everything
+we require, so in these cases, we can add a callback to process the server response. The
+`PhoneDetailCtrl` controller illustrates this by setting the `mainImageUrl` in a callback.
+
+
+## Test
+
+We have modified our unit tests to verify that our new service is issuing HTTP requests and
+processing them as expected. The tests also check that our controllers are interacting with the
+service correctly.
+
+The {@link api/angular.module.ng.$resource $resource} service augments the response object with
+methods for updating and deleting the resource. If we were to use the standard `toEqual` matcher,
+our tests would fail because the test values would not match the responses exactly. To solve the
+problem, we use a newly-defined `toEqualData` {@link
+http://pivotal.github.com/jasmine/jsdoc/symbols/jasmine.Matchers.html Jasmine matcher}. When the
+`toEqualData` matcher compares two objects, it takes only object properties into account and
+ignores methods.
+
+
+__`test/unit/controllersSpec.js`:__
+<pre>
+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.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);
+ });
+
+ 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.module.ng.$rootScope.Scope();
+ $browser = scope.$service('$browser');
+ });
+
+ beforeEach(function() {
+ scope = angular.module.ng.$rootScope.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>
+
+To run the unit tests, execute the `./scripts/test.sh` script and you should see the following
+output.
+
+ Chrome: Runner reset.
+ ....
+ Total 4 tests (Passed: 4; Fails: 0; Errors: 0) (3.00 ms)
+ Chrome 11.0.696.57 Mac OS: Run 4 tests (Passed: 4; Fails: 0; Errors 0) (3.00 ms)
+
+
+# Summary
+
+There you have it! We have created a web app in a relatively short amount of time. In the {@link
+the_end closing notes} we'll cover were to go from here.
+
+
+<ul doc:tutorial-nav="11"></ul>
diff --git a/docs/content/tutorial/the_end.ngdoc b/docs/content/tutorial/the_end.ngdoc
new file mode 100644
index 00000000..ed6eda97
--- /dev/null
+++ b/docs/content/tutorial/the_end.ngdoc
@@ -0,0 +1,21 @@
+@ngdoc overview
+@name Tutorial: The End
+@description
+
+Our application is now complete. Feel free to experiment with the code further, and jump back to
+previous steps using the `git checkout` or `goto_step.sh` commands.
+
+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 code, see the {@link cookbook/ Cookbook}.
+
+When you are ready to start developing a project using angular, we recommend that you bootstrap
+your development with the {@link https://github.com/angular/angular-seed angular seed} project.
+
+We hope this tutorial was useful to you and that you learned enough about angular to make you want
+to learn more. We especially hope you are inspired to go out and develop angular web apps of your
+own, and that you might be interested in {@link misc/contribute contributing} to angular.
+
+If you have questions or feedback or just want to say "hi", please post a message at {@link
+https://groups.google.com/forum/#!forum/angular}.