diff options
Diffstat (limited to 'docs/content/tutorial/step_02.ngdoc')
| -rw-r--r-- | docs/content/tutorial/step_02.ngdoc | 203 |
1 files changed, 203 insertions, 0 deletions
diff --git a/docs/content/tutorial/step_02.ngdoc b/docs/content/tutorial/step_02.ngdoc new file mode 100644 index 00000000..ceeb0e92 --- /dev/null +++ b/docs/content/tutorial/step_02.ngdoc @@ -0,0 +1,203 @@ +@ngdoc overview +@name Tutorial: 2 - Angular Templates +@description + +<ul doc:tutorial-nav="2"></ul> + + +Now it's time to make the web page dynamic -- with Angular. We'll also add a test that verifies the +code for the controller we are going to add. + +There are many ways to structure the code for an application. For Angular apps, we encourage the +use of {@link http://en.wikipedia.org/wiki/Model–View–Controller the Model-View-Controller (MVC) +design pattern} to decouple the code and to separate concerns. With that in mind, let's use a +little Angular and JavaScript to add model, view, and controller components to our app. + + +<doc:tutorial-instructions step="2"></doc:tutorial-instructions> + + +The app now contains a list with three phones. + +The most important changes are listed below. You can see the full diff on {@link +https://github.com/angular/angular-phonecat/compare/step-1...step-2 GitHub}: + + +## Template for the View + +The __view__ component is constructed by Angular from this template: + +__`app/index.html`:__ +<pre> +<html ng:app> +... +<body ng:controller="PhoneListCtrl"> + + <ul> + <li ng:repeat="phone in phones"> + {{phone.name}} + <p>{{phone.snippet}}</p> + </li> + </ul> + + <script src="lib/angular/angular.js"></script> + <script src="js/controllers.js"></script> +</body> +</html> +</pre> + +We replaced the hard-coded phone list with the {@link api/angular.widget.@ng:repeat ng:repeat +widget} and two {@link guide/dev_guide.expressions Angular expressions} enclosed in curly braces: +`{{phone.name}}` and `{{phone.snippet}}`: + +* The `ng:repeat="phone in phones"` statement in the `<li>` tag is an Angular repeater. The +repeater tells Angular to create a `<li>` element for each phone in the list using the first `<li>` +tag as the template. + + <img src="img/tutorial/tutorial_02_final.png"> + +* The curly braces around `phone.name` and `phone.snippet` are examples of {@link +guide/dev_guide.compiler.markup Angular markup}. The curly markup is shorthand for the Angular +directive {@link api/angular.directive.ng:bind ng:bind}. An `ng:bind` directive indicates a +template binding point to Angular. Binding points are locations in a template where Angular creates +data-binding between the view and the model. + +In Angular, the view is a projection of the model through the HTML template. This means that +whenever the model changes, Angular refreshes the appropriate binding points, which updates the +view. + + +## Model and Controller + +The data __model__ (a simple array of phones in object literal notation) is instantiated within +the __controller__ function(`PhoneListCtrl`): + +__`app/js/controllers.js`:__ +<pre> +function PhoneListCtrl() { + this.phones = [{"name": "Nexus S", + "snippet": "Fast just got faster with Nexus S."}, + {"name": "Motorola XOOM™ with Wi-Fi", + "snippet": "The Next, Next Generation tablet."}, + {"name": "MOTOROLA XOOM™", + "snippet": "The Next, Next Generation tablet."}]; +} +</pre> + + + + +Although the controller is not yet doing very much controlling, it is playing a crucial role. By +providing context for our data model, the controller allows us to establish data-binding between +the model and the view. We connected the dots between the presentation, data, and logic components +as follows: + +* The name of our controller function(in the JavaScript file `controllers.js`) matches the {@link +api/angular.directive.ng:controller ng:controller} directive in the `<body>` tag (`PhoneListCtrl`). +* The data is instantiated within the *scope* of our controller function; our template binding +points are located within the block bounded by the `<body ng:controller="PhoneListCtrl">` tag. + + The concept of a scope in Angular is crucial; a scope can be seen as the glue which allows the +template, model and controller to work together. Angular uses scopes, along with the information +contained in the template, data model, and controller, to keep models and views separate, but in +sync. Any changes made to the model are reflected in the view; any changes that occur in the view +are reflected in the model. + + To learn more about Angular scopes, see the {@link api/angular.module.ng.$rootScope.Scope angular scope documentation}. + + +## Tests + +The "Angular way" makes it easy to test code as it is being developed. Take a look at the following +unit test for your newly created controller: + +__`test/unit/controllersSpec.js`:__ +<pre> +describe('PhoneCat controllers', function() { + + describe('PhoneListCtrl', function() { + + it('should create "phones" model with 3 phones', function() { + var ctrl = new PhoneListCtrl(); + expect(ctrl.phones.length).toBe(3); + }); + }); +}); +</pre> + +The test verifies that we have three records in the phones array and the example demonstrates how +easy it is to create a unit test for code in Angular. Since testing is such a critical part of +software development, we make it easy to create tests in Angular so that developers are encouraged +to write them. + +Angular developers prefer the syntax of Jasmine's Behavior-driven Development (BDD) framework when +writing tests. Although Angular does not require you to use Jasmine, we wrote all of the tests in +this tutorial in Jasmine. You can learn about Jasmine on the {@link +http://pivotal.github.com/jasmine/ Jasmine home page} and on the {@link +https://github.com/pivotal/jasmine/wiki Jasmine wiki}. + +The angular-seed project is pre-configured to run all unit tests using {@link +http://code.google.com/p/js-test-driver/ JsTestDriver}. To run the test, do the following: + +1. In a _separate_ terminal window or tab, go to the `angular-phonecat` directory and run +`./scripts/test-server.sh` to start the test web server. + +2. Open a new browser tab or window and navigate to {@link http://localhost:9876}. + +3. Choose "Capture this browser in strict mode". + + At this point, you can leave this tab open and forget about it. JsTestDriver will use it to +execute the tests and report the results in the terminal. + +4. Execute the test by running `./scripts/test.sh` + + You should see the following or similar output: + + Chrome: Runner reset. + . + Total 1 tests (Passed: 1; Fails: 0; Errors: 0) (2.00 ms) + Chrome 11.0.696.57 Mac OS: Run 1 tests (Passed: 1; Fails: 0; Errors 0) (2.00 ms) + + Yay! The test passed! Or not... + + Note: If you see errors after you run the test, close the browser tab and go back to the terminal +and kill the script, then repeat the procedure above. + +# Experiments + +* Add another binding to `index.html`. For example: + + <p>Total number of phones: {{phones.length}}</p> + +* Create a new model property in the controller and bind to it from the template. For example: + + this.hello = "Hello, World!" + + Refresh your browser to make sure it says, "Hello, World!" + +* Create a repeater that constructs a simple table: + + <table> + <tr><th>row number</th></tr> + <tr ng:repeat="i in [0, 1, 2, 3, 4, 5, 6, 7]"><td>{{i}}</td></tr> + </table> + + Now, make the list 1-based by incrementing `i` by one in the binding: + + <table> + <tr><th>row number</th></tr> + <tr ng:repeat="i in [0, 1, 2, 3, 4, 5, 6, 7]"><td>{{i+1}}</td></tr> + </table> + +* Make the unit test fail by changing the `toBe(3)` statement to `toBe(4)`, and rerun the +`./scripts/test.sh` script. + + +# Summary + +You now have a dynamic app that features separate model, view, and controller components, and you +are testing as you go. Now, let's go to {@link step_03 step 3} to learn how to add full text search +to the app. + + +<ul doc:tutorial-nav="2"></ul> |
