diff options
| author | Igor Minar | 2011-06-15 22:31:40 -0700 | 
|---|---|---|
| committer | Igor Minar | 2011-06-15 22:31:40 -0700 | 
| commit | b842642b574a2b95c53b791308ed1bf8ff9d304d (patch) | |
| tree | fb26431c5372be74de2105df77e94dea4f198489 /docs/content/tutorial | |
| parent | d428c9910e66246c2af46602499acaeaf187d75b (diff) | |
| download | angular.js-b842642b574a2b95c53b791308ed1bf8ff9d304d.tar.bz2 | |
docs - stripping extra new lines
Diffstat (limited to 'docs/content/tutorial')
| -rw-r--r-- | docs/content/tutorial/index.ngdoc | 24 | ||||
| -rw-r--r-- | docs/content/tutorial/step_00.ngdoc | 42 | ||||
| -rw-r--r-- | docs/content/tutorial/step_01.ngdoc | 20 | ||||
| -rw-r--r-- | docs/content/tutorial/step_02.ngdoc | 64 | ||||
| -rw-r--r-- | docs/content/tutorial/step_03.ngdoc | 56 | ||||
| -rw-r--r-- | docs/content/tutorial/step_04.ngdoc | 60 | ||||
| -rw-r--r-- | docs/content/tutorial/step_05.ngdoc | 66 | ||||
| -rw-r--r-- | docs/content/tutorial/step_06.ngdoc | 30 | ||||
| -rw-r--r-- | docs/content/tutorial/step_07.ngdoc | 61 | ||||
| -rw-r--r-- | docs/content/tutorial/step_08.ngdoc | 54 | ||||
| -rw-r--r-- | docs/content/tutorial/step_09.ngdoc | 28 | ||||
| -rw-r--r-- | docs/content/tutorial/step_10.ngdoc | 48 | ||||
| -rw-r--r-- | docs/content/tutorial/step_11.ngdoc | 60 | ||||
| -rw-r--r-- | docs/content/tutorial/the_end.ngdoc | 6 | 
14 files changed, 1 insertions, 618 deletions
| diff --git a/docs/content/tutorial/index.ngdoc b/docs/content/tutorial/index.ngdoc index 68793134..605bccd3 100644 --- a/docs/content/tutorial/index.ngdoc +++ b/docs/content/tutorial/index.ngdoc @@ -2,20 +2,16 @@  @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 angular 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"> -  As you work through this tutorial, you will learn how angular makes browsers smarter — without the  use of extensions or plugins. -  * 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.  * You will see how angular creates listeners on your data without the need for DOM manipulation. @@ -23,13 +19,10 @@ dynamic views of data that change immediately in response to user actions.  * You will 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 modifications! -  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 angular @@ -37,12 +30,10 @@ When you finish the tutorial you will be able to:  * Create and run tests  * Identify resources for learning more about angular -  The tutorial is will guide you through the process of building a simple application, including  writing and running unit and end-to-end tests, and will allow you to experiment with angular and  the application through experiments suggested at the end of each step. -  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 angular, check out the  {@link misc/started Getting Started} document. @@ -53,23 +44,14 @@ really digging into it. If you're looking for a shorter introduction to angular, - - - - - - -  # Working with the code -  There are two ways that you can you follow this tutorial and hack on the code, both available on  Mac/Linux or Windows environment. The first work flow uses Git versioning system for source code  management, the second work flow doesn't depend on any source control system and instead uses  scripts to copy snapshots of project files into your workspace (`sandbox`) directory. Choose the  one you prefer: -  <doc:tutorial-instructions show="true">    <doc:tutorial-instruction id="git-mac" title="Git on Mac/Linux">      <ol> @@ -95,7 +77,6 @@ server.</p></li>      </ol>    </doc:tutorial-instruction> -    <doc:tutorial-instruction id="git-win" title="Git on Windows">      <ol>        <li><p>Verify that you have <a href="http://java.com/">Java</a> installed and that the @@ -123,7 +104,6 @@ href="http://node-js.prcn.co.cc/">pre-compiled binaries</a>, unzip them and add      </ol>    </doc:tutorial-instruction> -    <doc:tutorial-instruction id="ss-mac" title="Snapshots on Mac/Linux">      <ol>        <li><p>Verify that you have <a href="http://java.com/">Java</a> installed by running the @@ -144,7 +124,6 @@ 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 @@ -167,9 +146,6 @@ href="http://node-js.prcn.co.cc/">pre-compiled binaries</a>, unzip them and add    </doc:tutorial-instruction>  </doc:tutorial-instructions> -  For either work flow you'll also need a web browser and your favorite text editor. -  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 index 09b06109..a8243d47 100644 --- a/docs/content/tutorial/step_00.ngdoc +++ b/docs/content/tutorial/step_00.ngdoc @@ -2,19 +2,14 @@  @name Tutorial: 0 - angular-seed  @description -  <ul doc:tutorial-nav="0"></ul> - -  You are now ready to build the 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> @@ -25,7 +20,6 @@ angular-seed, and run the application in the browser.        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> @@ -50,8 +44,6 @@ directory.</li>    </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> @@ -84,8 +76,6 @@ directory.</li>    </doc:tutorial-instruction> - -    <doc:tutorial-instruction id="ss-mac" title="Snapshots on Mac/Linux">      <ol>        <li><p>In angular-phonecat directory, run this command:</p> @@ -118,8 +108,6 @@ href="http://localhost:8000/app/index.html">http://localhost:8000/app/index.html    </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 angular-phonecat directory):</p> @@ -153,15 +141,11 @@ href="http://localhost:8000/app/index.html">http://localhost:8000/app/index.html  </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> @@ -173,10 +157,8 @@ __`app/index.html`:__  </head>  <body> -    Nothing here yet! -    <script src="lib/angular/angular.js" ng:autobind></script>  </body>  </html> @@ -184,73 +166,51 @@ __`app/index.html`:__ - - -  ## What is the code doing? -  * xmlns declaration -            <html xmlns:ng="http://angularjs.org"> -    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" ng:autobind> -    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:autobind ng:autobind} attribute. If angular finds  `ng:autobind`, it creates a root scope for the application and associates it with the `<html>`  element of the template: - -          <img src="img/tutorial/tutorial_00_final.png"/> - +  <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 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 index 81d9082a..37d7e0a6 100644 --- a/docs/content/tutorial/step_01.ngdoc +++ b/docs/content/tutorial/step_01.ngdoc @@ -2,34 +2,24 @@  @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>  ... @@ -51,27 +41,17 @@ __`app/index.html`:__  </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 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 index 1715990b..abed3977 100644 --- a/docs/content/tutorial/step_02.ngdoc +++ b/docs/content/tutorial/step_02.ngdoc @@ -2,50 +2,36 @@  @name Tutorial: 2 - Angular Template  @description -  <ul doc:tutorial-nav="2"></ul> - -  Now it's time to make this 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. With angular, we encourage the use of  {@link http://en.wikipedia.org/wiki/Model–View–Controller the MVC design pattern} to decouple the  code and 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 3 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>  ...  <body ng:controller="PhoneListCtrl"> -    <ul>      <li ng:repeat="phone in phones">        {{phone.name}} @@ -53,27 +39,22 @@ __`app/index.html`:__      </li>    </ul> -    <script src="lib/angular/angular.js" ng:autobind></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.  It tells  angular to create a `<li>` element for each phone in the phones 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 an example 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}. The `ng:bind` directives indicate to @@ -83,15 +64,11 @@ of the model through the HTML template. This means that whenever the model chang  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() { @@ -107,22 +84,16 @@ function PhoneListCtrl() { - - - -  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. 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  api/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 scopes are a crucial concept in angular; you can think of scopes as the glue that makes the  template, model and controller all work together. Angular uses scopes, along with the information  contained in the template, data model, and controller, to keep the model and view separated but in @@ -131,23 +102,17 @@ reflected in the model. To learn more about angular scopes, see the {@link api/a  angular scope documentation}. - -  ## Tests -  The "Angular way" makes it easy for us to test as we develop; the unit test for your newly created  controller looks as follows: -  __`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); @@ -156,105 +121,76 @@ describe('PhoneCat controllers', function() {  });  </pre> -  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. The test verifies that we have 3 records in the  phones array. -  Angular developers prefer the syntax of Jasmine's Behavior-driven Development  (BDD) framework when  writing tests. Although Jasmine is not required by angular, we used 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}. -  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're testing as you go. Now, let's go to 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 index c4654998..1ebde553 100644 --- a/docs/content/tutorial/step_03.ngdoc +++ b/docs/content/tutorial/step_03.ngdoc @@ -2,53 +2,38 @@  @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, and 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. The phone list on the page changes depending on what a user types  into the search box. -  The most important changes 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 name="query"/> -    <ul class="phones">      <li ng:repeat="phone in phones.$filter(query)">        {{phone.name}} @@ -58,70 +43,54 @@ __`app/index.html`:__  ...  </pre> -  We added a standard HTML `<input>` tag and use angular's {@link api/angular.Array.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.Array.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);      }); @@ -129,64 +98,48 @@ describe('PhoneCat App', function() {  });  </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  https://docs.google.com/document/d/11L8htLKrh6c92foV71ytYpiKkeKpM4_a5-9c3HywfIc/edit?hl=en&pli=1#  angular's end-to-end test runner}. -  To run the end-to-end test, open 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. -  * 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() { @@ -201,30 +154,21 @@ and title elements:      });    </pre> -    Refresh the browser tab with end-to-end test runner to see the test fail. Now add a `div` or `p`  element with `id` `"status"` and content with the `query` binding into the `index.html` template to  make the test pass. -  * Add a `pause()` statement into an end-to-end test and rerun it.  You'll see the runner pausing,  giving you the opportunity to explore the state of your application displayed in the browser. The  app is live! Change the search query to prove it. This is great for troubleshooting end-to-end  tests. - -  # Summary -  With full text search under our belt and a test to verify it, let's go to 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 index 39bb3d51..48084980 100644 --- a/docs/content/tutorial/step_04.ngdoc +++ b/docs/content/tutorial/step_04.ngdoc @@ -2,38 +2,27 @@  @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 changes 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>  ... @@ -50,7 +39,6 @@ __`app/index.html`:__      </li>    </ul> -    <ul class="phones">      <li ng:repeat="phone in phones.$filter(query).$orderBy(orderProp)">        {{phone.name}} @@ -60,26 +48,20 @@ __`app/index.html`:__  ...  </pre> -  In the `index.html` template we made the following changes: -  * 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.Array.orderBy `$orderBy`} method to  further process the input into the repeater. `$orderBy` is a utility method similar to {@link  api/angular.Array.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 @@ -87,17 +69,12 @@ necessary! - - -  ## Controller -  __`app/js/controller.js`:__  <pre>  /* App Controllers */ -  function PhoneListCtrl() {    this.phones = [{"name": "Nexus S",                    "snippet": "Fast just got faster with Nexus S.", @@ -109,21 +86,17 @@ function PhoneListCtrl() {                    "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 @@ -133,39 +106,28 @@ 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');      }); @@ -174,49 +136,37 @@ describe('PhoneCat controllers', function() {  </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"]); @@ -224,36 +174,26 @@ __`test/e2e/scenarios.js`:__  ...  </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 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 index 68cc726a..1aac51f7 100644 --- a/docs/content/tutorial/step_05.ngdoc +++ b/docs/content/tutorial/step_05.ngdoc @@ -2,40 +2,29 @@  @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.service services} called {@link  api/angular.service.$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>  [ @@ -51,125 +40,96 @@ Following is a sample of the file:  </pre> - -  ## Controller -  We'll use angular's {@link api/angular.service.$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.service 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.scope();        $browser = scope.$service('$browser'); -        $browser.xhr.expectGET('phones/phones.json')            .respond([{name: 'Nexus S'},                      {name: 'Motorola DROID'}]); @@ -178,59 +138,46 @@ describe('PhoneCat controllers', function() {    });  </pre> -  We created the controller in the test environment, as follows: -  * We created a root scope object by calling `angular.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.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'); @@ -239,44 +186,31 @@ Finally, we verify that the default value of `orderProp` is set correctly:  });  </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 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 index e7fa1660..aa628a5d 100644 --- a/docs/content/tutorial/step_06.ngdoc +++ b/docs/content/tutorial/step_06.ngdoc @@ -2,41 +2,29 @@  @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>   [ @@ -52,11 +40,8 @@ __`app/phones/phones.json`__ (sample snippet):  </pre> - -  ## Template -  __`app/index.html`:__  <pre>  ... @@ -70,13 +55,11 @@ __`app/index.html`:__  ...  </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 `{{ exppression }}` markup literally, which it would have done if we had only specified @@ -84,11 +67,8 @@ an attribute binding in a regular `src` attribute (`<img src="{{phone.imageUrl}}  `ng:src` prevents the browser from making an http request to an invalid location. - -  ## Test -  __`test/e2e/scenarios.js`__:  <pre>  ... @@ -100,36 +80,26 @@ __`test/e2e/scenarios.js`__:  ...  </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 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 index cda63463..4f6a768e 100644 --- a/docs/content/tutorial/step_07.ngdoc +++ b/docs/content/tutorial/step_07.ngdoc @@ -2,52 +2,38 @@  @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.service.$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 @@ -55,106 +41,83 @@ http://en.wikipedia.org/wiki/Deep_linking deep linking}, which lets us utilize t  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 `/phone`. 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.service.$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>  ...  <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> -  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"> @@ -170,7 +133,6 @@ __`app/partials/phone-list.html`:__    </li>  </ul> -  <ul class="phones">    <li ng:repeat="phone in phones.$filter(query).$orderBy(orderProp)">      <a href="#/phones/{{phone.id}}">{{phone.name}}</a> @@ -180,31 +142,23 @@ __`app/partials/phone-list.html`:__  </ul>  </pre> -  <img src="img/tutorial/tutorial_07_final.png"> -  We also added a placeholder template for the phone details view: -  __`app/partials/phone-list.html`:__  <pre>  TBD: detail view for {{params.phoneId}}  </pre> -  Note how we are using `params` model defined in the `PhoneCanCtrl` 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() { @@ -213,17 +167,13 @@ to various URLs and verify that the correct view was rendered.    });  ... -   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');     }); @@ -231,40 +181,29 @@ to various URLs and verify that the correct view was rendered.  </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 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 index a430387b..e81a8d93 100644 --- a/docs/content/tutorial/step_08.ngdoc +++ b/docs/content/tutorial/step_08.ngdoc @@ -2,43 +2,31 @@  @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.service.$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>  { @@ -62,71 +50,53 @@ __`app/phones/nexus-s.json`:__ (sample snippet)  </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> @@ -143,17 +113,13 @@ __`app/partials/phone-details.html`:__  </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>  ... @@ -162,46 +128,36 @@ __`test/unit/controllerSpec.js`:__        $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');      }); @@ -210,33 +166,23 @@ __`test/e2e/scenarios.js`:__  </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  https://docs.google.com/document/d/11L8htLKrh6c92foV71ytYpiKkeKpM4_a5-9c3HywfIc/edit?hl=en&pli=1#  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 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 index fc9ba360..0d5da766 100644 --- a/docs/content/tutorial/step_09.ngdoc +++ b/docs/content/tutorial/step_09.ngdoc @@ -2,44 +2,31 @@  @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.filter `angular.filter`} API. -  __`app/js/filters.js`:__  <pre>  angular.filter('checkmark', function(input) { @@ -47,21 +34,16 @@ angular.filter('checkmark', function(input) {  });  </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>  ... @@ -70,19 +52,14 @@ __`app/index.html`:__  ...  </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>  ... @@ -96,19 +73,14 @@ __`app/partials/phone-detail.html`:__  </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.filter.checkmark(true)).toBe('\u2713');      expect(angular.filter.checkmark(false)).toBe('\u2718'); diff --git a/docs/content/tutorial/step_10.ngdoc b/docs/content/tutorial/step_10.ngdoc index 109f5b77..510eacd4 100644 --- a/docs/content/tutorial/step_10.ngdoc +++ b/docs/content/tutorial/step_10.ngdoc @@ -2,80 +2,59 @@  @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)"> @@ -84,53 +63,40 @@ __`app/partials/phone-detail.html`:__  ...  </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');      }); @@ -138,51 +104,37 @@ __`test/e2e/scenarios.js`:__  });  </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(name) = 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 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 index 892342b5..e328aecd 100644 --- a/docs/content/tutorial/step_11.ngdoc +++ b/docs/content/tutorial/step_11.ngdoc @@ -2,42 +2,30 @@  @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.service.$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>  ... @@ -45,10 +33,8 @@ __`app/index.html`.__  ...  </pre> -  ## Service -  __`app/js/services.js`.__  <pre>   angular.service('Phone', function($resource) { @@ -58,36 +44,29 @@ __`app/js/services.js`.__   });  </pre> -  We used the {@link api/angular.service} 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.service.$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.service.$xhr $xhr} service. - -  ## Controller -  We simplified our sub-controllers (`PhoneListCtrl` and `PhoneDetailCtrl`) by factoring out the  lower-level {@link api/angular.service.$xhr $xhr} service, replacing it with a new service called  `Phone`. Angular's {@link api/angular.service.$resource `$resource`} service is easier to use than  {@link api/angular.service.$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(); @@ -95,40 +74,30 @@ function PhoneListCtrl(Phone_) {  //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 @@ -136,22 +105,17 @@ object, which will be filled with data when the xhr response returns. Because of  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.service.$resource $resource} client 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 @@ -161,13 +125,10 @@ http://pivotal.github.com/jasmine/jsdoc/symbols/jasmine.Matchers.html Jasmine ma  ignores methods. - -  __`test/unit/controllersSpec.js`:__  <pre>  describe('PhoneCat controllers', function() { -    beforeEach(function(){      this.addMatchers({        toEqualData: function(expected) { @@ -176,92 +137,71 @@ 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).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> -  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. - -  <ul doc:tutorial-nav="11"></ul> - diff --git a/docs/content/tutorial/the_end.ngdoc b/docs/content/tutorial/the_end.ngdoc index fd96a920..ed6eda97 100644 --- a/docs/content/tutorial/the_end.ngdoc +++ b/docs/content/tutorial/the_end.ngdoc @@ -2,26 +2,20 @@  @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}. | 
