From 0a604bdb90e9dff9399bae0fb4941fe46cc7e9f9 Mon Sep 17 00:00:00 2001 From: Kenneth R. Culp Date: Tue, 26 Apr 2011 09:54:08 -0700 Subject: Tutorial files for your perusal. --- docs/tutorial.ngdoc | 87 ++++++++++++++++++++++++ docs/tutorial.step_1.ngdoc | 86 ++++++++++++++++++++++++ docs/tutorial.step_10.ngdoc | 109 ++++++++++++++++++++++++++++++ docs/tutorial.step_11.ngdoc | 113 +++++++++++++++++++++++++++++++ docs/tutorial.step_2.ngdoc | 133 ++++++++++++++++++++++++++++++++++++ docs/tutorial.step_3.ngdoc | 104 +++++++++++++++++++++++++++++ docs/tutorial.step_4.ngdoc | 114 +++++++++++++++++++++++++++++++ docs/tutorial.step_5.ngdoc | 109 ++++++++++++++++++++++++++++++ docs/tutorial.step_6.ngdoc | 101 ++++++++++++++++++++++++++++ docs/tutorial.step_7.ngdoc | 159 ++++++++++++++++++++++++++++++++++++++++++++ docs/tutorial.step_8.ngdoc | 110 ++++++++++++++++++++++++++++++ docs/tutorial.step_9.ngdoc | 95 ++++++++++++++++++++++++++ docs/tutorial_intro.ngdoc | 155 ++++++++++++++++++++++++++++++++++++++++++ 13 files changed, 1475 insertions(+) create mode 100755 docs/tutorial.ngdoc create mode 100755 docs/tutorial.step_1.ngdoc create mode 100644 docs/tutorial.step_10.ngdoc create mode 100644 docs/tutorial.step_11.ngdoc create mode 100755 docs/tutorial.step_2.ngdoc create mode 100755 docs/tutorial.step_3.ngdoc create mode 100755 docs/tutorial.step_4.ngdoc create mode 100755 docs/tutorial.step_5.ngdoc create mode 100755 docs/tutorial.step_6.ngdoc create mode 100755 docs/tutorial.step_7.ngdoc create mode 100755 docs/tutorial.step_8.ngdoc create mode 100755 docs/tutorial.step_9.ngdoc create mode 100644 docs/tutorial_intro.ngdoc diff --git a/docs/tutorial.ngdoc b/docs/tutorial.ngdoc new file mode 100755 index 00000000..41b53ccf --- /dev/null +++ b/docs/tutorial.ngdoc @@ -0,0 +1,87 @@ +@workInProgress +@ngdoc overview +@name Tutorial +@description + + + + + + + + + +
Previous{@link http://angular.github.com/angular-phonecat/step-0/app Example}{@link tutorial Tutorial Home}Code Diff{@link tutorial.step_1 Next}
+ +Welcome to the angular tutorial! Before you begin, you can check out the finished app here: +{@link http://angular.github.com/angular-phonecat/step-11/app/ The Completed Tutorial App}. Also, +if you missed the {@link tutorial_intro Intro to Tutorial} doc, it provides some background info, +and describes different options you have in working through the tutorial. + +We'll begin the tutorial by creating a basic page, and then we'll add functionality to our app on +each subsequent step. + +# Step 0 +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 and some key angular {@link angular.directive +directives}. + +__`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 bootstrap angular 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_intro Intro to +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 at the bottom of +the page). + +Now we can move on and add some content to our developing web app. + + + + + + + + + +
Previous{@link http://angular.github.com/angular-phonecat/step-0/app Example}{@link tutorial Tutorial Home}Code Diff{@link tutorial.step_1 Next}
diff --git a/docs/tutorial.step_1.ngdoc b/docs/tutorial.step_1.ngdoc new file mode 100755 index 00000000..02688f2e --- /dev/null +++ b/docs/tutorial.step_1.ngdoc @@ -0,0 +1,86 @@ +@workInProgress +@ngdoc overview +@name Tutorial: Step 1 +@description + + + + + + + + +
{@link tutorial Previous}{@link http://angular.github.com/angular-phonecat/step-1/app Example}{@link tutorial Tutorial Home} +{@link https://github.com/angular/angular-phonecat/commit/fa2a351f0ede1666041e407c52e4e5daf448c5f8 +Code Diff}{@link tutorial.step_2 Next}
+ +In this step, we will add basic information about two cell phones. + +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_intro Using Git:}__ + + From your `angular-phonecat` directory, run this command: + + git checkout step-1 + + * __{@link tutorial_intro Using Snapshots:}__ + + From `[install directory]/sandbox`, run this command: + + ./goto_step.sh 1 + +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 Previous}{@link http://angular.github.com/angular-phonecat/step-1/app Example}{@link tutorial Tutorial Home} +{@link https://github.com/angular/angular-phonecat/commit/fa2a351f0ede1666041e407c52e4e5daf448c5f8 +Code Diff}{@link tutorial.step_2 Next}
diff --git a/docs/tutorial.step_10.ngdoc b/docs/tutorial.step_10.ngdoc new file mode 100644 index 00000000..a993bee1 --- /dev/null +++ b/docs/tutorial.step_10.ngdoc @@ -0,0 +1,109 @@ +@workInProgress +@ngdoc overview +@name Tutorial: Step 10 +@description + + + + + + + + +
{@link tutorial.step_9 Previous}{@link http://angular.github.com/angular-phonecat/step-10/app Example}{@link tutorial Tutorial Home}{@link +https://github.com/angular/angular-phonecat/commit/abe1e13c7d9e725fdd3b811ca5ec28ea0d973aab Code +Diff}{@link tutorial.step_11 Next}
+ +In this step we will add a phone image swapping feature. We want to be able to click on a +thumbnail image in the phone details page, and have that action change the large phone image to +match the selection. + +__`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 "0.large" 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 variable 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 `ng:click` handler for thumb images to use the `setImage` controller method. +- And of course, we added e2e tests for our new feature. + + + + + + + + + + +
{@link tutorial.step_9 Previous}{@link http://angular.github.com/angular-phonecat/step-10/app Example}{@link tutorial Tutorial Home}{@link +https://github.com/angular/angular-phonecat/commit/abe1e13c7d9e725fdd3b811ca5ec28ea0d973aab Code +Diff}{@link tutorial.step_11 Next}
diff --git a/docs/tutorial.step_11.ngdoc b/docs/tutorial.step_11.ngdoc new file mode 100644 index 00000000..b770caea --- /dev/null +++ b/docs/tutorial.step_11.ngdoc @@ -0,0 +1,113 @@ +@workInProgress +@ngdoc overview +@name Tutorial: Step 11 +@description + + + + + + + + +
{@link tutorial.step_10 Previous}{@link http://angular.github.com/angular-phonecat/step-11/app Example}{@link tutorial Tutorial Home}{@link +https://github.com/angular/angular-phonecat/commit/46e2bc3ff21a1385d6ef1860c5c242f8e0265379 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 +object. Using this client object we can make requests for data in an easier way, without having +to deal with the lower-level {@link angular.service.$xhr $xhr} APIs. + +__`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 PhonesCtrl($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);
+}
+//PhonesCtrl.$inject = ['$route'];
+
+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'];
+
+
+ + +## 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. + +* 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. + +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 Example}{@link tutorial Tutorial Home}{@link +https://github.com/angular/angular-phonecat/commit/46e2bc3ff21a1385d6ef1860c5c242f8e0265379 Code +Diff}Next
diff --git a/docs/tutorial.step_2.ngdoc b/docs/tutorial.step_2.ngdoc new file mode 100755 index 00000000..f78cb2aa --- /dev/null +++ b/docs/tutorial.step_2.ngdoc @@ -0,0 +1,133 @@ +@workInProgress +@ngdoc overview +@name Tutorial: Step 2 +@description + + + + + + + + +
{@link tutorial.step_1 Previous}{@link http://angular.github.com/angular-phonecat/step-2/app Example}{@link tutorial Tutorial Home}{@link + https://github.com/angular/angular-phonecat/commit/02e30dd64e0e5554fbf4d442ade5b1a251f2bf56 + Code Diff}{@link tutorial.step_3 Next}
+ +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. + +Our __View__ component is constructed by angular from this template: + +__`app/index.html`:__ +
+...
+
+
+  
+
+  
+  
+
+...
+
+ +Our data __Model__ (a small set 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 constructs two-way + data-binding between the View and the Model. In angular, the View is a projection of the Model + through the HTML template. + +* __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 two-way 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_1 Previous} + {@link http://angular.github.com/angular-phonecat/step-2/app Example} + {@link tutorial Tutorial Home} + {@link + https://github.com/angular/angular-phonecat/commit/02e30dd64e0e5554fbf4d442ade5b1a251f2bf56 + Code Diff} + {@link tutorial.step_3 Next} + + diff --git a/docs/tutorial.step_3.ngdoc b/docs/tutorial.step_3.ngdoc new file mode 100755 index 00000000..6ebe81e8 --- /dev/null +++ b/docs/tutorial.step_3.ngdoc @@ -0,0 +1,104 @@ +@workInProgress +@ngdoc overview +@name Tutorial: Step 3 +@description + + + + + + + + +
    {@link tutorial.step_2 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_4 Next}
    + +In this step, we will add full text search to our app. 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 very nice features in angular. In this example, + the data that you type into the input box (named __`query`__) is immediately available as a + filter in the list repeater (`phone in phones.$filter(`__`query`__`)`). When the page loads, + angular binds the name of the input box to a variable of the same name in the data model. + Whenever the data Model changes, the View reflects the change, and vice versa. + + * Use of `$filter` in a template. The `$filter` function is one of several built-in utility + functions that augment JavaScript arrays during their evaluation as angular expressions. An + {@link Angular.array angular array} is a JavaScript array object with additional functionality + added. In {@link guide.expression angular expressions}, these array utilities are available as + methods. (They are prefixed with a $ to avoid naming collisions.) + + * How `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:__ 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_2 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_4 Next}
    diff --git a/docs/tutorial.step_4.ngdoc b/docs/tutorial.step_4.ngdoc new file mode 100755 index 00000000..260ad38f --- /dev/null +++ b/docs/tutorial.step_4.ngdoc @@ -0,0 +1,114 @@ +@workInProgress +@ngdoc overview +@name Tutorial: Step 4 +@description + + + + + + + + +
    {@link tutorial.step_3 Previous}{@link http://angular.github.com/angular-phonecat/step-4/app Example}{@link tutorial Tutorial Home}{@link +https://github.com/angular/angular-phonecat/commit/b56c91f453114347f0cb25e70b1c4fa4f1421763 Code +Diff}{@link tutorial.step_5 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');
    +    });
    +  });
    +});
    +
    + +## 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: + +* Like {@link angular.Array.filter $filter}, {@link angular.Array.orderBy $orderBy} is a built-in +method available on array objects in angular expressions. In our UI template, we set up a select +box that lets the user set the `orderProp` model variable to one of the string constants: `age` or +`name`. + +* In our controller, we added a line to set the default value of `orderProp` to `age`. + +* Our unit test now verifies that our default ordering property is set. + +* Once again we added a little more CSS to improve the View. + + + + + + + + + +
    {@link tutorial.step_3 Previous}{@link http://angular.github.com/angular-phonecat/step-4/app Example}{@link tutorial Tutorial Home}{@link +https://github.com/angular/angular-phonecat/commit/b56c91f453114347f0cb25e70b1c4fa4f1421763 Code +Diff}{@link tutorial.step_5 Next}
    diff --git a/docs/tutorial.step_5.ngdoc b/docs/tutorial.step_5.ngdoc new file mode 100755 index 00000000..3480e437 --- /dev/null +++ b/docs/tutorial.step_5.ngdoc @@ -0,0 +1,109 @@ +@workInProgress +@ngdoc overview +@name Tutorial: Step 5 +@description + + + + + + + + +
    {@link tutorial.step_4 Previous}{@link http://angular.github.com/angular-phonecat/step-5/app Example}{@link tutorial Tutorial Home}{@link + https://github.com/angular/angular-phonecat/commit/4f0a518557b5c7442568666b211aa79499870276 Code + Diff}{@link tutorial.step_6 Next}
    + +In this step, the View template remains the same but the Model and Controller change. We'll +introduce the use of an angular {@link angular.service service}, which we will use to implement an +`HttpXmlRequest` request to communicate with a server. Angular provides the built-in {@link +angular.service.$xhr $xhr} service for this purpose. + +The addition of the `$xhr` service to our app gives us the opportunity to talk about {@link +guide.di Dependency Injection} (DI). The use of DI is another cornerstone of the angular +philosophy. DI helps make your web apps well structured, loosely coupled, and ultimately easier to +test. + +__`app/js/controllers.js:`__ +
    +/* 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'];
    +
    + +__`test/unit/controllerSpec.js`:__ +
    +/* jasmine specs for controllers go here */
    +describe('PhoneCat controllers', function() {
    +
    +  describe('PhoneListCtrl', function(){
    +    var scope, $browser, ctrl;
    +
    +    beforeEach(function() {
    +      scope = angular.scope();
    +      $browser = scope.$service('$browser');
    +
    +      $browser.xhr.expectGET('phones/phones.json').respond([{name: 'Nexus S'},
    +                                                            {name: 'Motorola DROID'}]);
    +      ctrl = scope.$new(PhoneListCtrl);
    +    });
    +
    +
    +    it('should create "phones" model with 2 phones fetched from xhr', function() {
    +      expect(ctrl.phones).toBeUndefined();
    +      $browser.xhr.flush();
    +
    +      expect(ctrl.phones).toEqual([{name: 'Nexus S'},
    +                                   {name: 'Motorola DROID'}]);
    +    });
    +
    +
    +    it('should set the default value of orderProp model', function() {
    +      expect(ctrl.orderProp).toBe('age');
    +    });
    +  });
    +});
    +
    + +## Discussion: + +* __Services:__ {@link angular.service Services} are substitutable objects managed by angular's +{@link guide.di DI subsystem}. Angular services simplify some of the standard operations common +to web apps. Angular provides several built-in services (such as {@link angular.service.$xhr +$xhr}). You can also create your own custom services. + +* __Dependency Injection:__ To use an angular service, you simply provide the name of the service +as a parameter to the function in which you are using that service. Angular's {@link guide.di DI +subsystem} recognizes the identity of the service by name, provides it for you when you need it, +and manages any transitive dependencies the service may have (services often depend upon other +services). + +* __`$xhr`:__ We moved our data set out of the controller and into the file `phones/phones.json`. +This file serves as our data store rather than an actual server (to the browser they look the +same). We now use the `$xhr` service to return our phone data, which you'll note is still within +the scope of our controller function. + +* __Testing:__ The unit test has been expanded. It now verifies that the `$xhr` service behaves +as expected. + + + + + + + + + +
    {@link tutorial.step_4 Previous}{@link http://angular.github.com/angular-phonecat/step-5/app Example}{@link tutorial Tutorial Home}{@link + https://github.com/angular/angular-phonecat/commit/4f0a518557b5c7442568666b211aa79499870276 Code + Diff}{@link tutorial.step_6 Next}
    diff --git a/docs/tutorial.step_6.ngdoc b/docs/tutorial.step_6.ngdoc new file mode 100755 index 00000000..dc7b07ed --- /dev/null +++ b/docs/tutorial.step_6.ngdoc @@ -0,0 +1,101 @@ +@workInProgress +@ngdoc overview +@name Tutorial: Step 6 +@description + + + + + + + + +
    {@link tutorial.step_5 Previous}{@link http://angular.github.com/angular-phonecat/step-6/app Example}{@link tutorial Tutorial Home}{@link +https://github.com/angular/angular-phonecat/commit/2fb113a4da9b6d19e17627f351f0681befcccdc0 Code +Diff}{@link tutorial.step_7 Next}
    + +In this step, we add thumbnail images, links, and a little more CSS to our app. For now, our +links go nowhere. One step at a time; in the next step we'll implement new views that these links +will open. + +__`app/index.html`:__ +
    +...
    +  
    +
    +  
    +...
    +
    + +__`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?"
    +  }, 
    +  …
    + ]
    +
    + +## 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. + + + + + + + + + +
    {@link tutorial.step_5 Previous}{@link http://angular.github.com/angular-phonecat/step-6/app Example}{@link tutorial Tutorial Home}{@link +https://github.com/angular/angular-phonecat/commit/2fb113a4da9b6d19e17627f351f0681befcccdc0 Code +Diff}{@link tutorial.step_7 Next}
    diff --git a/docs/tutorial.step_7.ngdoc b/docs/tutorial.step_7.ngdoc new file mode 100755 index 00000000..3b5984b4 --- /dev/null +++ b/docs/tutorial.step_7.ngdoc @@ -0,0 +1,159 @@ +@workInProgress +@ngdoc overview +@name Tutorial: Step 7 +@description + + + + + + + + +
    {@link tutorial.step_6 Previous}{@link http://angular.github.com/angular-phonecat/step-7/app Example}{@link tutorial Tutorial Home}{@link +https://github.com/angular/angular-phonecat/commit/43ff5d76f1c0a464da67d691418e33e6c9d8dbc8 Code +Diff}{@link tutorial.step_8 Next}
    + +In this step we introduce angular's {@link angular.service.$route $route} service. This service +is usually used in conjunction with the {@link angular.widget.ng:view ng:view} directive. The +`$route` service makes it easy to wire together controllers, View templates, and the current URL +location in the browser. Using this feature we can implement {@link +http://en.wikipedia.org/wiki/Deep_linking deep linking}, which lets us utilize the browser's +History, and Back and Forward browser navigation. + +We'll use {@link angular.service.$route $route} to implement two different views for our +application: one view presents the phone listing, and the other view presents the details for a +particular phone. We'll use {@link angular.widget.ng:view ng:view} to include one or the other of +those views in our main layout page (`index.html`). The view presented in the layout page is based +on which URL the user navigates to. + +To manage our two different views, we'll move the existing phone list controller into a +sub-controller, add a second sub-controller to handle the phone details, and we'll create a new +root controller to implement the routing. (We'll save the implementation of the phone details +View for the next step.) + +__`app/index.html`:__ +
    +
    +...
    +
    +  
    +...
    +
    + +__`app/partials/phone-list.html`:__ +
    +
    +
    +
    +
    + +__`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: + +We have many changes to discuss here in Step 7: + +* __The View.__ Our View template in `index.html` has been reduced down to this: +``. It is now what we call a "layout template", because it contains +information common for all views, including the layout of our application. The {@link +angular.widget.ng:view ng:view} directive behaves like an "include" declaration (it's a +specialized sibling of the {@link angular.widget.ng:include ng:include} directive) that works +specifically with the {@link angular.service.$route $route} service. The View template associated +with the current route definition gets included "between those tags" (there's more to it than a +simple include, but that explanation will do for now). + + * We added two new View templates: + + * `app/partials/phone-list.html` for the phone list; + + * `app/partials/phone-detail.html` for the phone details (just a stub for this step); + +* __The Controller(s).__ We now have a new root controller (`PhoneCatCtrl`) and two +sub-controllers (`PhoneListCtrl` and `PhoneDetailCtrl`). + + * __`$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` 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 + variable `:phoneId` in the `path` parameter of `$route.when()`: `$route` stores that + portion of the current URL fragment in its current parameters in a property called + `params.phoneId`. We made the `$route` parameters available to our sub-controllers in the + `$route.onChange()` function in our root controller. We will use the `phoneId` property + when we fetch the phone details in Step 8. + + * Any other URL fragment gets redirected to `/phones`. + + * __Deep Linking.__ In `$route`'s `onChange()` method, we copied {@link + http://en.wikipedia.org/wiki/Deep_linking deep linking} parameters to the `params` property in + the root scope, so we can use them in the child scopes managed by our sub-controllers. + + + + + + + + + +
    {@link tutorial.step_6 Previous}{@link http://angular.github.com/angular-phonecat/step-7/app Example}{@link tutorial Tutorial Home}{@link +https://github.com/angular/angular-phonecat/commit/43ff5d76f1c0a464da67d691418e33e6c9d8dbc8 Code +Diff}{@link tutorial.step_8 Next}
    diff --git a/docs/tutorial.step_8.ngdoc b/docs/tutorial.step_8.ngdoc new file mode 100755 index 00000000..bf03fdac --- /dev/null +++ b/docs/tutorial.step_8.ngdoc @@ -0,0 +1,110 @@ +@workInProgress +@ngdoc overview +@name Tutorial: Step 8 +@description + + + + + + + + +
    {@link tutorial.step_7 Previous}{@link http://angular.github.com/angular-phonecat/step-8/app Example}{@link tutorial Tutorial Home}{@link +https://github.com/angular/angular-phonecat/commit/1f91f571bdd6f1e705ebb303998afe7820ffc6d9 Code +Diff}{@link tutorial.step_9 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"
    +  }
    +}
    +
    + +## 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. + +* Note how we used the `$route` `params` object from the scope managed by the root controller +(`PhoneCatCtrl`), to construct the path to the phone details requested by the user. 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. + + + + + + + + + +
    {@link tutorial.step_7 Previous}{@link http://angular.github.com/angular-phonecat/step-8/app Example}{@link tutorial Tutorial Home}{@link +https://github.com/angular/angular-phonecat/commit/1f91f571bdd6f1e705ebb303998afe7820ffc6d9 Code +Diff}{@link tutorial.step_9 Next}
    diff --git a/docs/tutorial.step_9.ngdoc b/docs/tutorial.step_9.ngdoc new file mode 100755 index 00000000..9fcf095d --- /dev/null +++ b/docs/tutorial.step_9.ngdoc @@ -0,0 +1,95 @@ +@workInProgress +@ngdoc overview +@name Tutorial: Step 9 +@description + + + + + + + + +
    {@link tutorial.step_8 Previous}{@link http://angular.github.com/angular-phonecat/step-9/app Example}{@link tutorial Tutorial Home}{@link +https://github.com/angular/angular-phonecat/commit/975d173ad0768487852387497c086f3c93fb48f6 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.) do not handle what we want to do, and 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 images: ✓ 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) +
    +/* http://docs.angularjs.org/#!angular.filter */
    +
    +angular.filter('checkmark', function(input) {
    +  return input ? '\u2713' : '\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 add your filter function on to the `angular.filter` object. + +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`). + + + + + + + + + +
    {@link tutorial.step_8 Previous}{@link http://angular.github.com/angular-phonecat/step-9/app Example}{@link tutorial Tutorial Home}{@link +https://github.com/angular/angular-phonecat/commit/975d173ad0768487852387497c086f3c93fb48f6 Code +Diff}{@link tutorial.step_10 Next}
    diff --git a/docs/tutorial_intro.ngdoc b/docs/tutorial_intro.ngdoc new file mode 100644 index 00000000..855f545c --- /dev/null +++ b/docs/tutorial_intro.ngdoc @@ -0,0 +1,155 @@ +@workInProgress +@ngdoc overview +@name Intro to Tutorial +@description + +A great way to get introduced to angular is to work through the {@link Tutorial angular tutorial}, +which walks you through the construction of an angular web app. The app you will build in the +tutorial is based on the {@link http://www.google.com/phone/# Google phone gallery 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 +* Create your own angular widgets and directives +* Add your own tags to 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) from the angular server. +* 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: + +* 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. 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. + +1. 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`. + +1. 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). + +1. To see the app running in your browser, do the following: + * __For node.js users:__ + 1. Run `./scripts/web-server.js` to start the web server. + 1. Open a browser window for the app and navigate to http://localhost:8000/app/index.html. + 1. 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. Start the server. + 1. Navigate in your browser to + http://localhost:[*port-number*]/[*context-path*]/app/index.html. + 1. 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. + +1. Navigate to [*the angular server*]. + +1. Download and unzip [*the snapshot file*] to a suitable location. + +1. Change directories to [*install-dir*]/sandbox. + +1. 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. + 1. Open a browser window for the app and navigate to http://localhost:8000/app/index.html. + 1. 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. + 1. Start the server. + 1. Navigate in your app browser to + http://localhost:[*port-number*]/[*context-path*]/app/index.html. + 1. 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 {@link Tutorial +the tutorial} and inspect the tutorial files on our servers; doing this will give you a good idea +of what angular does. + +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 -- cgit v1.2.3