2011-05-02 17:16:50 +00:00
|
|
|
|
@ngdoc overview
|
|
|
|
|
|
@name Tutorial: Step 2
|
|
|
|
|
|
@description
|
|
|
|
|
|
<table id="tutorial_nav">
|
|
|
|
|
|
<tr>
|
|
|
|
|
|
<td id="previous_step">{@link tutorial.step_01 Previous}</td>
|
|
|
|
|
|
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-2/app Live
|
|
|
|
|
|
Demo}</td>
|
|
|
|
|
|
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
|
|
|
|
|
<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-1...step-2 Code
|
|
|
|
|
|
Diff}</td>
|
|
|
|
|
|
<td id="next_step">{@link tutorial.step_03 Next}</td>
|
|
|
|
|
|
</tr>
|
|
|
|
|
|
</table>
|
|
|
|
|
|
|
|
|
|
|
|
Now it's time to make this web page dynamic with angular. We'll also add a test that verifies the
|
|
|
|
|
|
code for the controller we are going to add.
|
|
|
|
|
|
|
|
|
|
|
|
There are many ways to structure the code for an application. With angular, we encourage the use
|
|
|
|
|
|
of {@link http://en.wikipedia.org/wiki/Model–View–Controller the MVC design pattern} to decouple
|
|
|
|
|
|
the code and separate concerns. With that in mind, let's use a little angular and JavaScript to
|
|
|
|
|
|
add Model, View, and Controller components to our app.
|
|
|
|
|
|
|
|
|
|
|
|
1. Reset your workspace to Step 2 using:
|
|
|
|
|
|
|
|
|
|
|
|
git checkout --force step-2
|
|
|
|
|
|
or
|
|
|
|
|
|
|
|
|
|
|
|
./goto_step.sh 2
|
|
|
|
|
|
|
|
|
|
|
|
2. Refresh your browser or check the app out on {@link
|
|
|
|
|
|
http://angular.github.com/angular-phonecat/step-2/app our server}. The app now contains a list
|
|
|
|
|
|
with 3 phones.
|
|
|
|
|
|
|
|
|
|
|
|
The most important changes are listed below. You can see the full diff on {@link
|
|
|
|
|
|
https://github.com/angular/angular-phonecat/compare/step-1...step-2 GitHub}:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Template for the View
|
|
|
|
|
|
|
|
|
|
|
|
The __View__ component is constructed by angular from this template:
|
|
|
|
|
|
|
|
|
|
|
|
__`app/index.html`:__
|
|
|
|
|
|
<pre>
|
|
|
|
|
|
...
|
|
|
|
|
|
<body ng:controller="PhoneListCtrl">
|
|
|
|
|
|
|
|
|
|
|
|
<ul>
|
|
|
|
|
|
<li ng:repeat="phone in phones">
|
|
|
|
|
|
{{phone.name}}
|
|
|
|
|
|
<p>{{phone.snippet}}</p>
|
|
|
|
|
|
</li>
|
|
|
|
|
|
</ul>
|
|
|
|
|
|
|
|
|
|
|
|
<script src="lib/angular/angular.js" ng:autobind></script>
|
|
|
|
|
|
<script src="js/controllers.js"></script>
|
|
|
|
|
|
</body>
|
|
|
|
|
|
</html>
|
|
|
|
|
|
</pre>
|
|
|
|
|
|
|
|
|
|
|
|
We replaced the hard-coded phone list with the {@link 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 `<li>` tag is an angular repeater. It
|
|
|
|
|
|
tells angular to create a `<li>` element for each phone in the phones list, using the first
|
|
|
|
|
|
`<li>` tag as the template.
|
|
|
|
|
|
|
|
|
|
|
|
* 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.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Model and Controller
|
|
|
|
|
|
|
|
|
|
|
|
The data __Model__ (a short list 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. 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 `<body>` tag
|
|
|
|
|
|
(`PhoneListCtrl`).
|
|
|
|
|
|
* We instantiated our data within the scope of our controller function, and our template
|
|
|
|
|
|
binding points are located within the block bounded by the `<body
|
|
|
|
|
|
ng:controller="PhoneListCtrl>` tag.
|
|
|
|
|
|
|
|
|
|
|
|
Angular uses scopes, along with the information contained in the template, data model, and
|
|
|
|
|
|
controller to keep the Model and View separated but in sync: any changes to the model are
|
|
|
|
|
|
reflected in the view; any changes that occur in the view are reflected in the model.
|
|
|
|
|
|
|
|
|
|
|
|
As for our data model, we created a simple array of phone records, specified in object literal
|
|
|
|
|
|
notation.
|
|
|
|
|
|
|
|
|
|
|
|
## Tests
|
|
|
|
|
|
|
|
|
|
|
|
The "Angular way" makes it easy for us to test as we develop; the unit test for your newly created
|
|
|
|
|
|
controller looks as follows:
|
|
|
|
|
|
|
|
|
|
|
|
__`test/unit/controllersSpec.js`:__
|
|
|
|
|
|
<pre>
|
|
|
|
|
|
describe('PhoneCat controllers', function() {
|
|
|
|
|
|
|
|
|
|
|
|
describe('PhoneListCtrl', function(){
|
|
|
|
|
|
|
|
|
|
|
|
it('should create "phones" model with 3 phones', function() {
|
|
|
|
|
|
var ctrl = new PhoneListCtrl();
|
|
|
|
|
|
expect(ctrl.phones.length).toBe(3);
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
</pre>
|
|
|
|
|
|
|
|
|
|
|
|
Ease of testing is another cornerstone of angular's design philosophy. All we are doing here is
|
|
|
|
|
|
showing how easy it is to create a unit test. The test verifies that we have 3 records in the
|
|
|
|
|
|
phones array.
|
|
|
|
|
|
|
|
|
|
|
|
Angular developers prefer the syntax of Jasmine's Behavior-driven Development (BDD) framework when
|
|
|
|
|
|
writing tests. Although Jasmine is not required by angular, we used it to write all tests in this
|
|
|
|
|
|
tutorial. You can learn about Jasmine on the {@link http://pivotal.github.com/jasmine/ Jasmine
|
|
|
|
|
|
home page} and on the {@link https://github.com/pivotal/jasmine/wiki Jasmine wiki}.
|
|
|
|
|
|
|
|
|
|
|
|
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, navigate to http://localhost:9876, and choose "strict
|
|
|
|
|
|
mode". At this point, you can leave this tab open and forget about it. JsTestDriver will
|
|
|
|
|
|
use it to execute our tests and report the results in the terminal.
|
|
|
|
|
|
|
|
|
|
|
|
3. 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! Now, let's go to Step 3 to learn how to add full text search to the app.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<table id="tutorial_nav">
|
|
|
|
|
|
<tr>
|
|
|
|
|
|
<td id="previous_step">{@link tutorial.step_01 Previous}</td>
|
|
|
|
|
|
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-2/app Live
|
|
|
|
|
|
Demo}</td>
|
|
|
|
|
|
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
|
|
|
|
|
<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-1...step-2 Code
|
|
|
|
|
|
Diff}</td>
|
|
|
|
|
|
<td id="next_step">{@link tutorial.step_03 Next}</td>
|
|
|
|
|
|
</tr>
|
|
|
|
|
|
</table>
|