From 11e9572b952e49b01035e956c412d6095533031a Mon Sep 17 00:00:00 2001 From: Misko Hevery Date: Fri, 29 Apr 2011 15:18:27 -0700 Subject: Move documentation under individual headings --- docs/content/tutorial/index.ngdoc | 172 ++++++++++++++++++++++++++++++++++ docs/content/tutorial/step_00.ngdoc | 77 +++++++++++++++ docs/content/tutorial/step_01.ngdoc | 88 ++++++++++++++++++ docs/content/tutorial/step_02.ngdoc | 137 +++++++++++++++++++++++++++ docs/content/tutorial/step_03.ngdoc | 108 +++++++++++++++++++++ docs/content/tutorial/step_04.ngdoc | 161 ++++++++++++++++++++++++++++++++ docs/content/tutorial/step_05.ngdoc | 147 +++++++++++++++++++++++++++++ docs/content/tutorial/step_06.ngdoc | 113 ++++++++++++++++++++++ docs/content/tutorial/step_07.ngdoc | 181 ++++++++++++++++++++++++++++++++++++ docs/content/tutorial/step_08.ngdoc | 148 +++++++++++++++++++++++++++++ docs/content/tutorial/step_09.ngdoc | 108 +++++++++++++++++++++ docs/content/tutorial/step_10.ngdoc | 110 ++++++++++++++++++++++ docs/content/tutorial/step_11.ngdoc | 178 +++++++++++++++++++++++++++++++++++ 13 files changed, 1728 insertions(+) create mode 100644 docs/content/tutorial/index.ngdoc create mode 100755 docs/content/tutorial/step_00.ngdoc create mode 100755 docs/content/tutorial/step_01.ngdoc create mode 100755 docs/content/tutorial/step_02.ngdoc create mode 100755 docs/content/tutorial/step_03.ngdoc create mode 100755 docs/content/tutorial/step_04.ngdoc create mode 100755 docs/content/tutorial/step_05.ngdoc create mode 100755 docs/content/tutorial/step_06.ngdoc create mode 100755 docs/content/tutorial/step_07.ngdoc create mode 100755 docs/content/tutorial/step_08.ngdoc create mode 100755 docs/content/tutorial/step_09.ngdoc create mode 100644 docs/content/tutorial/step_10.ngdoc create mode 100644 docs/content/tutorial/step_11.ngdoc (limited to 'docs/content/tutorial') diff --git a/docs/content/tutorial/index.ngdoc b/docs/content/tutorial/index.ngdoc new file mode 100644 index 00000000..b430b248 --- /dev/null +++ b/docs/content/tutorial/index.ngdoc @@ -0,0 +1,172 @@ +@workInProgress +@ngdoc overview +@name Tutorial +@description + +A great way to get introduced to angular is to work through the {@link tutorial.step_00 angular +tutorial}, which walks you through the construction of an angular web app. The app you will build +in the tutorial is loosely based on the {@link http://www.google.com/phone/ Google phone gallery +app}. The {@link http://angular.github.com/angular-phonecat/step-11/app/ end result of our effort} +is visually simpler, but demonstrates many of the angular features without distractions in the +form of CSS code. + +This tutorial app ends up like a Google phone gallery app, but is originally based on the {@link +https://github.com/angular/angular-seed angular-seed project}. The angular seed app isn't +necessary for building angular apps, but it helps you get started quickly and makes the +development and testing process much easier. Angular-seed includes a simple example, the latest +angular libraries, test libraries, and scripts. It provides all of these in an environment that +is pre-configured for developing a typical web app. + +Once you set up your tutorial environment, you should be able to get through the material in less +than a day and you'll have fun doing it. More experienced coders may be able to zip through the +exercises in an afternoon. In any case, we promise that your time will be well spent! + +When you finish the tutorial you will be able to: + +* Create a simple dynamic application that works in any browser +* Define the differences between angular and common JavaScript frameworks +* Understand angular expressions +* Understand how data binding works in angular +* Use the angular-seed project to quickly boot-strap your own projects +* Create and run tests +* Identify resources for learning more about angular + +You can work through the tutorial in any of the following ways: + +* Using Git. Use the Git versioning system to get the files for each step. +* Using Snapshots. Download snapshots (files for each step of the +tutorial) and tinker with them. +* Reading the Examples. Read through the examples, and inspect +results and code on our server. + +The first two ways (Git and snapshots) give you a fuller experience, in that you can run the unit +and end-to-end tests in addition to the tutorial app. They also give you the ability to play +around with the code and get instant feedback in your browser. The last way (reading through the +tutorial online) requires no setup on your machine, but you can't run the tests, and it won't be +as easy to play around with the code. + + +# Prerequisites for Git and Snapshots + +To run the tutorial app and tests on your machine (using Git or the snapshots) you will need the +following: + +* You need to be running on a Mac or Linux machine. +* An http server running on your system. If you don't already have one installed, you can install +`node.js` ({@link https://github.com/joyent/node/wiki/Installation node.js install}) or another +http sever (such as Apache, etc.). +* Java. This is required for running tests. Angular itself doesn't require Java. +* A modern browser (including IE8+). Needed for viewing and debugging code. +* A text editor of your choice. + + +# Using Git + +The following instructions are for developers who are comfortable with Git's versioning system: + +1. Check to be sure you have all of the prerequisites on your system. + +2. Clone the angular-phonecat repository located at {@link +https://github.com/angular/angular-phonecat angular-phonecat} by running the following command in +a terminal: + + git clone git://github.com/angular/angular-phonecat.git + + This will create a directory called `angular-phonecat`. + +3. In terminal, navigate to the `angular-phonecat` directory and run: + + git checkout step-0 + + (You can run `git checkout step-[0-11]` to go to any of the steps in the tutorial). + +4. To see the app running in a browser, do the following: + * __For node.js users:__ + 1. Run `./scripts/web-server.js` to start the app server. + 2. Open a browser window for the app and navigate to http://localhost:8000/app/index.html. + + * __For other http servers:__ + 1. Configure the server to serve the files in the `angular-phonecat` directory. + 2. Run `./scripts/web-server.js` to start the app server. + 3. Navigate in your browser to + http://localhost:[*port-number*]/[*context-path*]/app/index.html. + +5. To see tests running in a browser, do the following: + * __For node.js users:__ + 1. Run `./scripts/test-server.sh` to start the test web server. + 2. Open a browser window for the tests, navigate to http://localhost:9876, and choose + "strict mode". + * __For other http servers:__ + 1. Configure the server to serve the files in the `angular-phonecat` directory. + 1. Run `./scripts/test-server.sh` to start the test web server. + 3. Navigate in your browser to http://localhost:[*port-number*]/, and choose "strict mode". + + + + +# Using Snapshots + +Snapshots are the sets of files that reflect the state of the tutorial app at each step. These +files include the HTML, CSS, and JavaScript for the app, plus Jasmine JavaScript files and Java +libraries for the test stack. These will let you run the tutorial app and tests, without requiring +knowledge of Git. You can download and install the snapshot files as follows: + +1. Check to be sure you have all of the prerequisites on your system. + +2. Navigate to [*the angular server*], and download and unzip [*the snapshot file*] to an +[*install-dir*] of your choosing. + +3. Change directories to [*install-dir*]/sandbox. + +4. Run the following command: + * `./goto_step.sh 0` + + You have to start out at the beginning, which is Step 0. After you set up Step 0, you can skip + around between any steps. + +1. To see the app running in your browser, do the following: + * __For node.js users:__ + 1. Run `./scripts/web-server.js` to run the web server. + 2. Open a browser window for the app and navigate to http://localhost:8000/app/index.html. + 3. Open a browser window for the tests, navigate to http://localhost:9876, and choose + "strict mode". + + * __For other http servers:__ + 1. Configure servers to serve the app and test files in the [*install-dir*]/sandbox. + 2. Start the server. + 3. Navigate in your app browser to + http://localhost:[*port-number*]/[*context-path*]/app/index.html. + 4. Navigate in your test browser to http://localhost:[*port-number*] and choose "strict + mode". + +1. To view the tutorial app at different steps, run `./goto_step.sh [0-11]` and then refresh your +browser. For example, say you're on Step 5 of the tutorial, and you want to see the app in action: + + 1. Run `goto_step.sh 5` from the command line in the `sandbox` directory. + 1. Refresh your app browser. + + +# Reading the Examples + +If you don't want to set up anything on your local machine, you can read through the tutorial and +inspect the tutorial files on our servers; doing this will give you a good idea of what angular +does, but you won't be able to make any code changes and experiment on your own. + +To see the running app at each tutorial step, click the "Example" link at the top or bottom of +each tutorial page. + +To view the code differences between tutorial steps, click the Code Diff link at top or bottom of +each tutorial page. Additions are highlighted in green; deletions are highlighted in red. + + +# Relative URLs +Throughout the tutorial, we use relative URLs to refer to files hosted on our local http server. +The absolute URL depends on your configuration. For example, if you are using the node.js server, +`app/index.html` translates to: + + http://localhost:8000/app/index.html + +If you are using your own http server running on port 8080 and the tutorial files are hosted at +`/angular_tutorial`, `app/index.html` translates to: + + http://localhost:8080/angular_tutorial/app/index.html diff --git a/docs/content/tutorial/step_00.ngdoc b/docs/content/tutorial/step_00.ngdoc new file mode 100755 index 00000000..e506fcaf --- /dev/null +++ b/docs/content/tutorial/step_00.ngdoc @@ -0,0 +1,77 @@ +@workInProgress +@ngdoc overview +@name Tutorial: Step 0 +@description + + + + + + + + + +
{@link tutorial Previous}{@link http://angular.github.com/angular-phonecat/step-0/app Example}{@link tutorial Tutorial Home}Code Diff{@link tutorial.step_01 Next}
+ +The following sample code is our starting point. It is a static HTML page that displays next to +nothing, but it has everything we need to proceed. You can think of this bit of code as our +prototype template, consisting of basic HTML tags with a pair of angular specific attributes. + +__`app/index.html`:__ +
+
+
+
+  
+  my angular app
+  
+
+
+
+  Nothing here yet!
+
+  
+
+
+
+ +## Discussion: + +Although our app doesn't appear to do anything dynamic, note the following: + +* __... `xmlns:ng="http://angularjs.org"` ...__ This `xmlns` declaration for the `ng` namespace +must be specified if you use XHTML, or if you are targeting IE older than 9 (regardless of whether +you are using XHTML or HTML). + +* __` + + This will download the angular script from the angular server instead of from a local file. + +* To try this code out in your browser, you need to navigate to the step-0 page (you are currently +on Step 0 of the tutorial). If your http server is running, navigate to `app/index.html`. +Remember, this is a relative URL (see the Relative URL section in {@link tutorial Tutorial}). The +browser will display the same thing as you would see if you go to +http://angular.github.com/angular-phonecat/step-0/app (accessible from Example link at the bottom +of the page). + +Now we can move on and add some content to our developing web app. + + + + + + + + + +
{@link tutorial Previous}{@link http://angular.github.com/angular-phonecat/step-0/app Example}{@link tutorial Tutorial Home}Code Diff{@link tutorial.step_01 Next}
diff --git a/docs/content/tutorial/step_01.ngdoc b/docs/content/tutorial/step_01.ngdoc new file mode 100755 index 00000000..e22adc20 --- /dev/null +++ b/docs/content/tutorial/step_01.ngdoc @@ -0,0 +1,88 @@ +@workInProgress +@ngdoc overview +@name Tutorial: Step 1 +@description + + + + + + + + +
{@link tutorial.step_00 Previous}{@link http://angular.github.com/angular-phonecat/step-1/app Example}{@link tutorial Tutorial Home} +{@link https://github.com/angular/angular-phonecat/compare/step-0...step-1 Code Diff}{@link tutorial.step_02 Next}
+ +Now that we have the basic ingredients in place, let's add some basic information about two cell +phones to our app. + +Note: We will usually include only the new code that we added for each step. In this and +subsequent examples, we will leave out code from the previous step that hasn't changed, for +example: + + ... + + ... + +Let's add the following code to `index.html`: + +__`app/index.html`:__ +
+
+...
+  Google Phone Gallery
+...
+
+...
+  
+...
+
+ +## Discussion: + +* It's a static web page! We displayed info about two phones! Yay. + +* For those of you playing along at home on your own web servers, did you switch to Step 1 and +refresh your browsers? + + * __{@link tutorial Using Git:}__ + + From your `angular-phonecat` directory, run this command: + + git checkout step-1 + + * __{@link tutorial Using Snapshots:}__ + + From `[install directory]/sandbox`, run this command: + + ./goto_step.sh 1 + +* Now would be a good time to open up `app/index.html` in your browser and see the current state +of our "application". It's not very exciting, but that's ok. + +When you're ready, let's move on and start using some angular features to turn this static page +into a dynamic web app. + + + + + + + + + +
{@link tutorial.step_00 Previous}{@link http://angular.github.com/angular-phonecat/step-1/app Example}{@link tutorial Tutorial Home} +{@link https://github.com/angular/angular-phonecat/compare/step-0...step-1 Code Diff}{@link tutorial.step_02 Next}
diff --git a/docs/content/tutorial/step_02.ngdoc b/docs/content/tutorial/step_02.ngdoc new file mode 100755 index 00000000..50fbd240 --- /dev/null +++ b/docs/content/tutorial/step_02.ngdoc @@ -0,0 +1,137 @@ +@workInProgress +@ngdoc overview +@name Tutorial: Step 2 +@description + + + + + + + + +
{@link tutorial.step_01 Previous}{@link http://angular.github.com/angular-phonecat/step-2/app Example}{@link tutorial Tutorial Home}{@link https://github.com/angular/angular-phonecat/compare/step-1...step-2 Code +Diff}{@link tutorial.step_03 Next}
+ +In the last step, we remembered what a basic, static web page looks like, and now we want to get +dynamic. There are many ways to do this, but an important feature of angular is the incorporation +of the principles behind {@link http://en.wikipedia.org/wiki/Model–View–Controller the MVC design +pattern} into client-side web apps. With that in mind, let's use a little angular and a little +JavaScript to add Model, View, and Controller components to our app, and change the static page +into one that is dynamically generated. + +Our __View__ component is constructed by angular from this template: + +__`app/index.html`:__ +
+...
+
+
+  
+
+  
+  
+
+...
+
+ +Our data __Model__ (a short list of phones in object literal notation) is instantiated within our +__Controller__ function (`PhoneListCtrl`): + +__`app/js/controllers.js`:__ +
+/* App Controllers */
+
+function PhoneListCtrl() {
+  this.phones = [{"name": "Nexus S",
+                  "snippet": "Fast just got faster with Nexus S."},
+                 {"name": "Motorola XOOM™ with Wi-Fi",
+                  "snippet": "The Next, Next Generation tablet."},
+                 {"name": "MOTOROLA XOOM™",
+                  "snippet": "The Next, Next Generation tablet."}];
+}
+
+ +The "Angular way" urges us to test as we develop: + +__`test/unit/controllersSpec.js`:__ +
+/* jasmine specs for controllers go here */
+describe('PhoneCat controllers', function() {
+
+  describe('PhoneListCtrl', function(){
+
+    it('should create "phones" model with 3 phones', function() {
+      var ctrl = new PhoneListCtrl();
+      expect(ctrl.phones.length).toBe(3);
+    });
+  });
+});
+
+ +## Discussion: + +So what were our changes from Step 1? + +* __View template:__ We replaced the hard-coded phone list with the {@link +angular.widget.@ng:repeat ng:repeat widget} and two {@link guide.expression angular expressions} +enclosed in curly braces: `{{phone.name}}` and `{{phone.snippet}}`: + + * The `ng:repeat="phone in phones"` statement in the `
  • ` tag is an angular repeater. It + tells angular to create a `
  • ` element for each phone in the phones list, using the first + `
  • ` tag as the template. + + * The curly braces around `phone.name` and `phone.snippet` are an example of {@link + angular.markup angular markup}. The curly braces are shorthand for the angular directive + {@link angular.directive.ng:bind ng:bind}. They indicate to angular that these are template + binding points. Binding points are locations in the template where angular creates + data-binding between the View and the Model. In angular, the View is a projection of the Model + through the HTML template. This means that whenever the model changes, angular refreshes the + appropriate binding points, which updates the view. + +* __Controller:__ At this point, it doesn't appear as if our controller is doing very much +controlling, but it is playing a crucial role: providing context for our data model so we can +establish data-binding between the model and the view. Note in the following how we connected the +dots between our presentation, data, and logic components: + + * The name of our controller function (in the JavaScript file `controllers.js`) matches the + {@link angular.directive.ng:controller ng:controller} directive in the `` 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 ` + + {@link tutorial.step_01 Previous} + {@link http://angular.github.com/angular-phonecat/step-2/app Example} + {@link tutorial Tutorial Home} +{@link https://github.com/angular/angular-phonecat/compare/step-1...step-2 Code +Diff} + {@link tutorial.step_03 Next} + + diff --git a/docs/content/tutorial/step_03.ngdoc b/docs/content/tutorial/step_03.ngdoc new file mode 100755 index 00000000..4333636d --- /dev/null +++ b/docs/content/tutorial/step_03.ngdoc @@ -0,0 +1,108 @@ +@workInProgress +@ngdoc overview +@name Tutorial: Step 3 +@description + + + + + + + + +
    {@link tutorial.step_02 Previous}{@link http://angular.github.com/angular-phonecat/step-3/app Example}{@link tutorial Tutorial Home}{@link + https://github.com/angular/angular-phonecat/commit/a03815f8fb00217f5f9c1d3ef83282f79818e706 Code + Diff}{@link tutorial.step_04 Next}
    + +We did a lot of work in laying the foundation of our app in the last step, so now we'll do +something simple, and add full text search. We will also write an end-to-end test, because a good +end-to-end test is a good friend. It stays with your app, keeps an eye on it, and quickly detects +regressions. + +__`app/index.html`:__ +
    +...
    +   Fulltext Search: 
    +
    +  
    +...
    +
    +__`test/e2e/scenarios.js`:__ +
    +/* jasmine-like end2end tests go here */
    +describe('PhoneCat App', function() {
    +
    +  describe('Phone list view', function() {
    +
    +    beforeEach(function() {
    +      browser().navigateTo('../../app/index.html');
    +    });
    +
    +
    +    it('should filter the phone list as user types into the search box', function() {
    +      expect(repeater('.phones li').count()).toBe(3);
    +
    +      input('query').enter('nexus');
    +      expect(repeater('.phones li').count()).toBe(1);
    +
    +      input('query').enter('motorola');
    +      expect(repeater('.phones li').count()).toBe(2);
    +    });
    +  });
    +});
    +
    + +## Discussion: + +We continued using the same controller that we set up in Step 2, but we added the following +features to our app: + +* __Search Box:__ A standard HTML `` tag combined with angular's {@link +angular.Array.filter $filter} utility (added to the repeater) lets a user type in search criteria +and immediately see the effects of their search on the phone list. This new code demonstrates the +following: + + * Two way Data-binding. This is one of the core features in angular. When the page loads, + angular binds the name of the input box to a variable of the same name in the data model and + keeps the two in sync. + +In this example, the data that you type into the input box (named __`query`__) is immediately +available as a filter input in the list repeater (`phone in phones.$filter(`__`query`__`)`). +Whenever the data model changes and this change causes the input to the repeater to change, the +repeater will efficiently update the DOM to reflect the current state of the model. + + * Use of `$filter` in a template. The `$filter` function is one of several built-in {@link + angular.Array angular functions} that augment JavaScript arrays during their evaluation as + angular expressions. In {@link guide.expression angular expressions}, these array utilities are + available as array methods. (They are prefixed with a $ to avoid naming collisions.) + + * `ng:repeat` automatically shrinks and grows the number of phones in the View, via DOM + manipulation that is completely transparent to the developer. If you've written any DOM + manipulation code, this should make you happy. + +* __CSS:__ We added in some minimal CSS to the file we set up in Step 0: `./css/app.css`. + +* __Testing:__ To run the end to end test, open http://localhost:8000/test/e2e/runner.html in +your browser. This end-to-end test shows the following: + + * Proof that the search box and the repeater are correctly wired together. + + * How easy it is to write end-to-end tests. This is just a simple test, but the point here is + to show how easy it is to set up a functional, readable, end-to-end test. + + + + + + + + + +
    {@link tutorial.step_02 Previous}{@link http://angular.github.com/angular-phonecat/step-3/app Example}{@link tutorial Tutorial Home}{@link + https://github.com/angular/angular-phonecat/commit/a03815f8fb00217f5f9c1d3ef83282f79818e706 Code + Diff}{@link tutorial.step_04 Next}
    diff --git a/docs/content/tutorial/step_04.ngdoc b/docs/content/tutorial/step_04.ngdoc new file mode 100755 index 00000000..0589ba75 --- /dev/null +++ b/docs/content/tutorial/step_04.ngdoc @@ -0,0 +1,161 @@ +@workInProgress +@ngdoc overview +@name Tutorial: Step 4 +@description + + + + + + + + +
    {@link tutorial.step_03 Previous}{@link http://angular.github.com/angular-phonecat/step-4/app Example}{@link tutorial Tutorial Home}{@link https://github.com/angular/angular-phonecat/compare/step-3...step-4 Code +Diff}{@link tutorial.step_05 Next}
    + +In this step, we add a feature that lets our users choose which way to order the phone list. + +__`app/index.html`:__ +
    +...
    +  
    +
    +  
    +...
    +
    + +__`app/js/controller.js`:__ +
    +/* 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';
    +}
    +
    + +__`test/unit/controllerSpec.js`:__ +
    +/* jasmine specs for controllers go here */
    +describe('PhoneCat controllers', function() {
    +
    +  describe('PhoneListCtrl', function(){
    +    var scope, $browser, ctrl;
    +
    +    beforeEach(function() {
    +      ctrl = new PhoneListCtrl();
    +    });
    +
    +
    +    it('should create "phones" model with 3 phones', function() {
    +      expect(ctrl.phones.length).toBe(3);
    +    });
    +
    +
    +    it('should set the default value of orderProp model', function() {
    +      expect(ctrl.orderProp).toBe('age');
    +    });
    +  });
    +});
    +
    + +__`test/e2e/scenarios.js`:__ +
    +/* jasmine-like end2end tests go here */
    +describe('PhoneCat App', function() {
    +
    +  describe('Phone list view', function() {
    +
    +    beforeEach(function() {
    +      browser().navigateTo('../../app/index.html');
    +    });
    +
    +
    +    it('should filter the phone list as user types into the search box', function() {
    +      expect(repeater('.phones li').count()).toBe(3);
    +
    +      input('query').enter('nexus');
    +      expect(repeater('.phones li').count()).toBe(1);
    +
    +      input('query').enter('motorola');
    +      expect(repeater('.phones li').count()).toBe(2);
    +    });
    +
    +
    +    it('should be possible to control phone order via the drop down select box', function() {
    +      input('query').enter('tablet'); //let's narrow the dataset to make the test assertions
    +      shorter
    +
    +      expect(repeater('.phones li', 'Phone List').column('a')).
    +          toEqual(["Motorola XOOM\u2122 with Wi-Fi",
    +                   "MOTOROLA XOOM\u2122"]);
    +
    +      select('orderProp').option('alphabetical');
    +
    +      expect(repeater('.phones li', 'Phone List').column('a')).
    +          toEqual(["MOTOROLA XOOM\u2122",
    +                   "Motorola XOOM\u2122 with Wi-Fi"]);
    +    });
    +  });
    +});
    +
    + +## Discussion: + +To provide dynamic ordering, we employ another one of angular's "array type augmenters" and let +the data binding do the rest of the work for us: + +* First, we provide a ` +
  • +
  • + Sort by: + +
  • + + + +... + + +__`app/js/controller.js`__ (Unchanged): +
    +/* App Controllers */
    +
    +function PhoneListCtrl($xhr) {
    +  var self = this;
    +
    +  $xhr('GET', 'phones/phones.json', function(code, response) {
    +    self.phones = response;
    +  });
    +
    +  self.orderProp = 'age';
    +}
    +
    +//PhoneListCtrl.$inject = ['$xhr'];
    +
    + +__`app/phones/phones.json`__ (sample snippet): +
    + [
    +  {
    +   "age": 4,
    +   ...
    +   "carrier": "T-Mobile",
    +   "id": "motorola-defy-with-motoblur",
    +   "imageUrl": "http://google.com/phone/image/small/640001",
    +   "name": "Motorola DEFY\u2122 with MOTOBLUR\u2122",
    +   "snippet": "Are you ready for everything life throws your way?"
    +  },
    +  …
    + ]
    +
    + +__`test/e2e/scenarios.js`__: +
    +...
    +    it('should render phone specific links', function() {
    +      input('query').enter('nexus');
    +      element('.phones li a').click();
    +      expect(browser().location().hash()).toBe('/phones/nexus-s');
    +    });
    +...
    +
    + +## Discussion: + +* Note that we're using {@link guide.expression angular expressions} enclosed in the now-familiar +{@link angular.markup double-curly brace markup} in the href attribute values. These represent +attribute bindings, and work the same way as the bindings we saw in previous steps. + +* Note also the use of the {@link angular.directive.ng:src ng:src} directive in the `` tag. +That directive prevents the browser from treating the angular `{{ exppression }}` markup +literally, as it would do if we tried to use markup in a regular `src` attribute. Use `ng:src` to +keep the browser from eagerly making an extra http request to an invalid location. + +* We expanded our end-to-end test to verify that the app is generating correct links to the phone +views we will implement in the upcoming steps. + + + + + + + + + +
    {@link tutorial.step_05 Previous}{@link http://angular.github.com/angular-phonecat/step-6/app Example}{@link tutorial Tutorial Home}{@link https://github.com/angular/angular-phonecat/compare/step-5...step-6 Code +Diff}{@link tutorial.step_07 Next}
    diff --git a/docs/content/tutorial/step_07.ngdoc b/docs/content/tutorial/step_07.ngdoc new file mode 100755 index 00000000..802130c5 --- /dev/null +++ b/docs/content/tutorial/step_07.ngdoc @@ -0,0 +1,181 @@ +@workInProgress +@ngdoc overview +@name Tutorial: Step 7 +@description + + + + + + + + +
    {@link tutorial.step_06 Previous}{@link http://angular.github.com/angular-phonecat/step-7/app Live Demo +}{@link tutorial Tutorial Home}{@link https://github.com/angular/angular-phonecat/compare/step-6...step-7 Code +Diff}{@link tutorial.step_08 Next}
    + +Our app is slowly growing and becoming more complex. Up until now, the app provided our users with +just one view (the list of all phones), and all of our template code was located in the +`index.html` file. The next step in building our app is the addition of a view that will show +detailed information about each of the devices in our list. + +To add the detailed view, we could expand the `index.html` file to contain template code for both +views, but that would get messy very quickly. Instead, we are going to turn the `index.html` +template into what we call a "layout template". This is a template that is common for all views in +our application. Other "partial templates" are then included into this layout template depending +on the current "route" — the view that is currently displayed to the user. + +Similarly as with templates, angular also allows for controllers and scopes managed by these +controllers to be nested. We are going to create a "root" controller called `PhoneCatCtrl`, which +will contain the declaration of routes for the application. + +Application routes in angular are declared via the {@link angular.service.$route $route} service. +This services makes it easy to wire together controllers, View templates, and the current URL +location in the browser. Using this feature we can implement {@link +http://en.wikipedia.org/wiki/Deep_linking deep linking}, which lets us utilize the browser's +History, and Back and Forward browser navigation. + +We'll use the $route service to declare that our application consists of two different views: one +view presents the phone listing, and the other view presents the details for a particular phone. +Each view will have the template stored in a separate file in the `app/partials/` directory. +Similarly each view will have a controller associated with it. These will be stored in the +existing `app/js/controllers.js` file. + +The `$route` service is usually used in conjunction with the {@link angular.widget.ng:view +ng:view} widget. The role of the `ng:view` widget is to include the view template for the current +route into the layout template, which makes it a perfect fit for our `index.html` template. + +For now we are going to get all the routing going, and move the phone listing template into a +separate file. We'll save the implementation of the phone details View for the next step. + +__`app/index.html`:__ +
    +...
    +
    +
    +  
    +
    +  
    +  
    +
    +
    +
    + +__`app/partials/phone-list.html`:__ +
    +
    +
    +
    +
    + +__`app/partials/phone-list.html`:__ +
    +TBD: detail view for {{params.phoneId}}
    +
    + +__`app/js/controller.js`:__ +
    +/* App Controllers */
    +
    +function PhoneCatCtrl($route) {
    +  var self = this;
    +
    +  $route.when('/phones',
    +              {template: 'partials/phone-list.html',   controller: PhoneListCtrl});
    +  $route.when('/phones/:phoneId',
    +              {template: 'partials/phone-detail.html', controller: PhoneDetailCtrl});
    +  $route.otherwise({redirectTo: '/phones'});
    +
    +  $route.onChange(function(){
    +    self.params = $route.current.params;
    +  });
    +
    +  $route.parent(this);
    +}
    +
    +//PhoneCatCtrl.$inject = ['$route'];
    +
    +
    +function PhoneListCtrl($xhr) {
    +  var self = this;
    +
    +  $xhr('GET', 'phones/phones.json', function(code, response) {
    +    self.phones = response;
    +  });
    +
    +  self.orderProp = 'age';
    +}
    +
    +//PhoneListCtrl.$inject = ['$xhr'];
    +
    +
    +function PhoneDetailCtrl() {}
    +
    + +## Discussion: + +* __The View.__ Our View template in `index.html` has been reduced down to this: +``. As described above, it is now a "layout template". We added the following +two new View templates: + + * `app/partials/phone-list.html` for the phone list. The phone-list view was formerly our + main view. We simply moved the code from `index.html` to here. + + * `app/partials/phone-detail.html` for the phone details (just a placeholder template for now). + +* __The Controller(s).__ We now have a new root controller (`PhoneCatCtrl`) and two +sub-controllers (`PhoneListCtrl` and `PhoneDetailCtrl`). These inherit the model properties and +behavior from the root controller. + + * __`$route.`__ The root controller's job now is to set up the `$route` configuration: + + * When the fragment part of the URL in the browser ends in "/phones", `$route` service + grabs the `phone-list.html` template, compiles it, and links it with a new scope that is + controlled by our `PhoneListCtrl` controller. + + * When the URL ends in "/phones/:phoneId", `$route` compiles and links the + `phone-detail.html` template as it did with `phone-list.html`. But note the use of the + `:phoneId` parameter declaration in the `path` argument of `$route.when()`: `$route` + services provides all the values for variables defined in this way as + `$route.current.params` map. In our route, `$route.current.params.phoneId` always holds + the current contents of the `:phoneId` portion of the URL. We will use the `phoneId` + parameter when we fetch the phone details in Step 8. + + * Any other URL fragment gets redirected to `/phones`. + + * __Controller/Scope inheritance.__ In the function passed into `$route`'s `onChange()` + method, we copied url parameters extracted from the current route to the `params` property in + the root scope. This property is inherited by child scopes created for our view controllers + and accessible by these controllers. + + * __Tests.__ To automatically verify that everything is wired properly, we write end to end + tests that navigate to various URLs and verify that the correct view was rendered. + + + + + + + + + +
    {@link tutorial.step_06 Previous}{@link http://angular.github.com/angular-phonecat/step-7/app Live Demo +}{@link tutorial Tutorial Home}{@link https://github.com/angular/angular-phonecat/compare/step-6...step-7 Code +Diff}{@link tutorial.step_08 Next}
    diff --git a/docs/content/tutorial/step_08.ngdoc b/docs/content/tutorial/step_08.ngdoc new file mode 100755 index 00000000..65ce6883 --- /dev/null +++ b/docs/content/tutorial/step_08.ngdoc @@ -0,0 +1,148 @@ +@workInProgress +@ngdoc overview +@name Tutorial: Step 8 +@description + + + + + + + + +
    {@link tutorial.step_07 Previous}{@link http://angular.github.com/angular-phonecat/step-8/app Live Demo +}{@link tutorial Tutorial Home}{@link https://github.com/angular/angular-phonecat/compare/step-7...step-8 Code +Diff}{@link tutorial.step_09 Next}
    + +In this step, we implement the Phone Details View template. Once again we will use {@link +angular.services.$xhr $xhr} to fetch our data, and we'll flesh out the `phone-details.html` View +template. + +__`app/partials/phone-details.html`:__ +
    +
    +
    +

    {{phone.name}}

    + +

    {{phone.description}}

    + + + + +
    + +__`app/js/controller.js`:__ +
    +function PhoneCatCtrl($route) (same as Step 7)
    +
    +function PhoneListCtrl($xhr) (same as Step 7)
    +
    +function PhoneDetailCtrl($xhr) {
    +  var self = this;
    +
    +  $xhr('GET', 'phones/' + self.params.phoneId + '.json', function(code, response) {
    +    self.phone = response;
    +  });
    +}
    +
    +//PhoneDetailCtrl.$inject = ['$xhr'];
    +
    + +__`app/phones/nexus-s.json`:__ (sample snippet) +
    +{
    +  "additionalFeatures": "Contour Display, Near Field Communications (NFC), Three-axis gyroscope,
    +  Anti-fingerprint display coating, Internet Calling support (VoIP/SIP)",
    +  "android": {
    +      "os": "Android 2.3",
    +      "ui": "Android"
    +  },
    +  ...
    +  "images": [
    +      "img/phones/nexus-s.0.jpg",
    +      "img/phones/nexus-s.1.jpg",
    +      "img/phones/nexus-s.2.jpg",
    +      "img/phones/nexus-s.3.jpg"
    +  ],
    +  "storage": {
    +      "flash": "16384MB",
    +      "ram": "512MB"
    +  }
    +}
    +
    + +__`test/unit/controllerSpec.js`:__ +
    +...
    +    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'});
    +    });
    +...
    +
    + +__`test/e2e/scenarios.js`:__ +
    +...
    +  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');
    +    });
    +  });
    +...
    +
    + +## Discussion: + +* Phone Details View Template. There is nothing fancy or new here, just note where we use the +angular `{{ expression }}` markup and directives to project phone data from our model into the +view. + +* Note how we used the `$route` `params` object from the scope managed by the root controller +(`PhoneCatCtrl`), to construct the path for the phone details xhr request. The rest of this step +is simply applying the previously learned concepts and angular APIs to create a large template +that displays a lot of data about a phone. + +* Tests. We updated the existing end to end test and wrote a new unit test that is similar in +spirit to the one we wrote for the `PhoneListCtrl` controller. + + + + + + + + + +
    {@link tutorial.step_07 Previous}{@link http://angular.github.com/angular-phonecat/step-8/app Live Demo +}{@link tutorial Tutorial Home}{@link https://github.com/angular/angular-phonecat/compare/step-7...step-8 Code +Diff}{@link tutorial.step_09 Next}
    diff --git a/docs/content/tutorial/step_09.ngdoc b/docs/content/tutorial/step_09.ngdoc new file mode 100755 index 00000000..2d6ed925 --- /dev/null +++ b/docs/content/tutorial/step_09.ngdoc @@ -0,0 +1,108 @@ +@workInProgress +@ngdoc overview +@name Tutorial: Step 9 +@description + + + + + + + + +
    {@link tutorial.step_08 Previous}{@link http://angular.github.com/angular-phonecat/step-9/app Live Demo +}{@link tutorial Tutorial Home}{@link https://github.com/angular/angular-phonecat/compare/step-8...step-9 Code +Diff}{@link tutorial.step_10 Next}
    + +In this step, we have determined that the built-in angular display filters ({@link +angular.filter.number number}, {@link angular.filter.currency currency}, {@link +angular.filter.date date}, etc.) don't handle what we want to do, so we get to create our own +custom {@link angular.filter filter}. + +In the previous step, the details page displayed either "true" or "false" to indicate whether +certain phone features were present or not. Our custom "checkmark" filter replaces those text +strings with glyphs: ✓ for "true", and ✘ for "false". + +Our filter code lives in `app/js/filters.js`: + +__`app/index.html`:__ +
    +...
    + 
    + 
    + 
    +...
    +
    + +In the phone details template, we employ our filter for angular expressions whose values are +"true" or "false"; `{{ [phone_feature] | checkmark }}`: + +__`app/partials/phone-detail.html`:__ +
    +
    +

    {{phone.name}}

    +

    {{phone.description}}

    +... + +
    + +__`app/js/filters.js`:__ (New) +
    +angular.filter('checkmark', function(input) {
    +  return input ? '\u2713' : '\u2718';
    +});
    +
    + +__`test/unit/filtersSpec.js`:__ (New) +
    +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');
    +  });
    +})
    +
    + +## Discussion: + +* This example shows how easy it is to roll your own filters for displaying data. As explained in +the "Writing your own Filters" section of the {@link angular.filter angular.filter} page, you +simply register your custom filter function on to the `angular.filter` function. + +* In this example, our filter name is "checkmark"; our input is either "true" or "false", and we +return one of two unicode characters we have chosen to represent true or false (`\u2713` and +`\u2718`). + +* We created a new unit test to verify that our custom filter converts boolean values to unicode +characters. + + + + + + + + + +
    {@link tutorial.step_08 Previous}{@link http://angular.github.com/angular-phonecat/step-9/app Live Demo +}{@link tutorial Tutorial Home}{@link https://github.com/angular/angular-phonecat/compare/step-8...step-9 Code +Diff}{@link tutorial.step_10 Next}
    diff --git a/docs/content/tutorial/step_10.ngdoc b/docs/content/tutorial/step_10.ngdoc new file mode 100644 index 00000000..130b4023 --- /dev/null +++ b/docs/content/tutorial/step_10.ngdoc @@ -0,0 +1,110 @@ +@workInProgress +@ngdoc overview +@name Tutorial: Step 10 +@description + + + + + + + + +
    {@link tutorial.step_09 Previous}{@link http://angular.github.com/angular-phonecat/step-10/app Live Demo +}{@link tutorial Tutorial Home}{@link https://github.com/angular/angular-phonecat/compare/step-9...step-10 +Code Diff}{@link tutorial.step_11 Next}
    + +The phone details view displays one large image of the current phone and several smaller thumbnail +images. It would be great if we could replace the large image with any of the thumbnails just by +clicking on the desired thumbnail image. Let's have a look how we can do this with angular. + +__`app/partials/phone-detail.html`:__ +
    +
    +
    +

    {{phone.name}}

    + +

    {{phone.description}}

    + + +... +
    + +__`app/js/controllers.js`:__ +
    +...
    +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'];
    +
    + +__`test/e2e/scenarios.js`:__ +
    +/* jasmine-like end2end tests go here */
    +...
    +  describe('Phone detail view', function() {
    +
    +    beforeEach(function() {
    +      browser().navigateTo('../../app/index.html#/phones/nexus-s');
    +    });
    +
    +
    +    it('should display nexus-s page', function() {
    +      expect(binding('phone.name')).toBe('Nexus S');
    +    });
    +
    +    it('should display the first phone image as the main phone image', function() {
    +       expect(element('img.phone').attr('src')).toBe('img/phones/nexus-s.0.jpg');
    +    });
    +
    +
    +    it('should swap main image if a thumbnail image is clicked on', function() {
    +      element('.phone-thumbs li:nth-child(3) img').click();
    +      expect(element('img.phone').attr('src')).toBe('img/phones/nexus-s.2.jpg');
    +
    +      element('.phone-thumbs li:nth-child(1) img').click();
    +      expect(element('img.phone').attr('src')).toBe('img/phones/nexus-s.0.jpg');
    +    });
    +  });
    +});
    +
    + +## Discussion: + +Adding the phone image swapping feature is fairly straightforward: + +* We defined the `mainImageUrl` model property in the details controller (`PhoneDetailCtrl`) and +set the default value of `mainImageUrl` to the first image in the array of images. +* We created a `setImage` controller method to change `mainImageUrl` to the image clicked on by +the user. +* We registered an `{@link angular.directive.ng:click ng:click}` handler for thumb images to use +the `setImage` controller method. +* We expanded the end-to-end test to verify that our new feature is swapping images correctly. + + + + + + + + + + +
    {@link tutorial.step_09 Previous}{@link http://angular.github.com/angular-phonecat/step-10/app Live Demo +}{@link tutorial Tutorial Home}{@link https://github.com/angular/angular-phonecat/compare/step-9...step-10 +Code Diff}{@link tutorial.step_11 Next}
    diff --git a/docs/content/tutorial/step_11.ngdoc b/docs/content/tutorial/step_11.ngdoc new file mode 100644 index 00000000..e383f406 --- /dev/null +++ b/docs/content/tutorial/step_11.ngdoc @@ -0,0 +1,178 @@ +@workInProgress +@ngdoc overview +@name Tutorial: Step 11 +@description + + + + + + + + +
    {@link tutorial.step_10 Previous}{@link http://angular.github.com/angular-phonecat/step-11/app Live Demo +}{@link tutorial Tutorial Home}{@link https://github.com/angular/angular-phonecat/compare/step-10...step-11 +Code Diff}Next
    + +And so we arrive at the last step of this tutorial. Here we define a custom service that +represents a {@link http://en.wikipedia.org/wiki/Representational_State_Transfer RESTful} client. +Using this client we can make xhr requests for data in an easier way, without having to deal with +the lower-level {@link angular.service.$xhr $xhr} APIs, HTTP methods and URLs. + +__`app/index.html`.__ +
    +...
    +  
    +...
    +
    + + +__`app/js/services.js`.__ (New) +
    + angular.service('Phone', function($resource){
    +  return $resource('phones/:phoneId.json', {}, {
    +    query: {method:'GET', params:{phoneId:'phones'}, isArray:true}
    +  });
    + });
    +
    + +__`app/js/controllers.js`.__ +
    +...
    +
    +function PhoneListCtrl(Phone_) {
    +  this.orderProp = 'age';
    +  this.phones = Phone_.query();
    +}
    +//PhoneListCtrl.$inject = ['Phone'];
    +
    +
    +function PhoneDetailCtrl(Phone_) {
    +  this.phone = Phone_.get({phoneId:this.params.phoneId});
    +}
    +//PhoneDetailCtrl.$inject = ['Phone'];
    +
    + +__`test/unit/controllersSpec.js`:__ +
    +/* jasmine specs for controllers go here */
    +describe('PhoneCat controllers', function() {
    +
    +  beforeEach(function(){
    +    this.addMatchers({
    +      toEqualData: function(expected) {
    +        return angular.equals(this.actual, expected);
    +      }
    +    });
    +  });
    +
    +  describe('PhoneListCtrl', function(){
    +    var scope, $browser, ctrl;
    +
    +    beforeEach(function() {
    +      scope = angular.scope();
    +      $browser = scope.$service('$browser');
    +
    +      $browser.xhr.expectGET('phones/phones.json').respond([{name: 'Nexus S'},
    +                                                            {name: 'Motorola DROID'}]);
    +      ctrl = scope.$new(PhoneListCtrl);
    +    });
    +
    +    it('should create "phones" model with 2 phones fetched from xhr', function() {
    +      expect(ctrl.phones).toEqual([]);
    +      $browser.xhr.flush();
    +
    +      expect(ctrl.phones).toEqualData([{name: 'Nexus S'},
    +                                       {name: 'Motorola DROID'}]);
    +    });
    +
    +    it('should set the default value of orderProp model', function() {
    +      expect(ctrl.orderProp).toBe('age');
    +    });
    +  });
    +
    +
    +  describe('PhoneDetailCtrl', function(){
    +    var scope, $browser, ctrl;
    +
    +    beforeEach(function() {
    +      scope = angular.scope();
    +      $browser = scope.$service('$browser');
    +    });
    +
    +    beforeEach(function() {
    +      scope = angular.scope();
    +      $browser = scope.$service('$browser');
    +    });
    +
    +    it('should fetch phone detail', function(){
    +      scope.params = {phoneId:'xyz'};
    +      $browser.xhr.expectGET('phones/xyz.json').respond({name:'phone xyz'});
    +      ctrl = scope.$new(PhoneDetailCtrl);
    +
    +      expect(ctrl.phone).toEqualData({});
    +      $browser.xhr.flush();
    +
    +      expect(ctrl.phone).toEqualData({name:'phone xyz'});
    +    });
    +  });
    +});
    +
    + + +## Discussion: + +* We simplified our sub-controllers (`PhoneListCtrl` and `PhoneDetailCtrl`) by factoring out the +lower-level `$xhr` service, replacing it with a new service called `Phone`. Angular's {@link +angular.service.$resource `$resource`} service is easier to use than `$xhr` for interacting with +data sources exposed as RESTful resources. It is also easier now to understand what the code in +our controllers is doing. + + An important thing to notice in our controller code is that we don't pass any callback + functions when invoking methods of our Phone services. It looks as if the result were returned + synchronously. That is not the case at all. What is returned synchronously is a "future" — an + object, which will be filled with data when the xhr response returns. Because of the + data-binding in angular, we can use this future and bind it to our template. Then, when the + data arrives, the view will automatically update. See? Angular tries hard to make simple + stuff simple. + +* Once again we make use of `$route's` params, this time to construct the URL passed as a +parameter to `$resource` in our `services.js` script. + +* Last, but certainly not least, we expanded and modified our unit test to verify that our new +service is returning data as we expect it to. + + In our assertions we use a newly-defined `toEqualData` {@link + http://pivotal.github.com/jasmine/jsdoc/symbols/jasmine.Matchers.html Jasmine matcher}, which + compares only object properties and ignores methods. This is necessary, because the `$resource` + client will augment the response object with handy methods for updating and deleting the + resource (we don't use these in our tutorial though). + +There you have it! We have created a web app in a relatively short amount of time. + +## Closing Notes: + +* For more details and examples of the angular concepts we touched on in this tutorial, see the +{@link guide Developer Guide}. + +* For several more examples of sample code, see the {@link cookbook Cookbook}. + +* When you are ready to start developing a project using angular, be sure to begin with the {@link +https://github.com/angular/angular-seed angular seed app}. + +* We hope this tutorial was useful to you, and that you learned enough about angular to make you +want to learn more. Of course, we especially hope you are inspired to go out and develop angular +web apps of your own, and perhaps you might even be interested in {@link contribute contributing} +to angular. + + + + + + + + + +
    {@link tutorial.step_10 Previous}{@link http://angular.github.com/angular-phonecat/step-11/app Live Demo +}{@link tutorial Tutorial Home}{@link https://github.com/angular/angular-phonecat/compare/step-10...step-11 +Code Diff}Next
    -- cgit v1.2.3