mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-16 23:30:23 +00:00
Tutorial files for your perusal.
This commit is contained in:
parent
4738d49e1c
commit
0a604bdb90
13 changed files with 1475 additions and 0 deletions
87
docs/tutorial.ngdoc
Executable file
87
docs/tutorial.ngdoc
Executable file
|
|
@ -0,0 +1,87 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Tutorial
|
||||
@description
|
||||
|
||||
<table id="tutorial_nav">
|
||||
<tr>
|
||||
<td id="previous_step">Previous</td>
|
||||
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-0/app Example}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">Code Diff</td>
|
||||
<td id="next_step">{@link tutorial.step_1 Next}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
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`:__
|
||||
<pre>
|
||||
<!doctype html>
|
||||
<html xmlns:ng="http://angularjs.org/">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>my angular app</title>
|
||||
<link rel="stylesheet" href="css/app.css"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
Nothing here yet!
|
||||
|
||||
<script src="lib/angular/angular.js" ng:autobind></script>
|
||||
</body>
|
||||
</html>
|
||||
</pre>
|
||||
|
||||
## 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).
|
||||
|
||||
* __`<script src="lib/angular/angular.js"` ...__ This downloads the `angular.js` bootstrap script
|
||||
and registers a callback that will be executed by the browser when the HTML is fully downloaded.
|
||||
When the callback is executed, angular looks for the {@link angular.directive.ng:autobind
|
||||
ng:autobind} attribute. If `ng:autobind` is found, it signals angular to compile and manage the
|
||||
whole page.
|
||||
|
||||
Note: If you elected not to download any tutorial files but still want to try out some angular
|
||||
code on your system, you can change the relative path to the `angular.js` script in your
|
||||
template from `./lib/angular/angular.js` to the following:
|
||||
|
||||
<script src="http://code.angularjs.org/angular-0.9.14.js" ng:autobind></script>
|
||||
|
||||
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.
|
||||
|
||||
<table id="tutorial_nav">
|
||||
<tr>
|
||||
<td id="previous_step">Previous</td>
|
||||
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-0/app Example}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">Code Diff</td>
|
||||
<td id="next_step">{@link tutorial.step_1 Next}</td>
|
||||
</tr>
|
||||
</table>
|
||||
86
docs/tutorial.step_1.ngdoc
Executable file
86
docs/tutorial.step_1.ngdoc
Executable file
|
|
@ -0,0 +1,86 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Tutorial: Step 1
|
||||
@description
|
||||
<table id="tutorial_nav">
|
||||
<tr>
|
||||
<td id="previous_step">{@link tutorial Previous}</td>
|
||||
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-1/app Example}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">
|
||||
{@link https://github.com/angular/angular-phonecat/commit/fa2a351f0ede1666041e407c52e4e5daf448c5f8
|
||||
Code Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_2 Next}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
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:
|
||||
|
||||
...
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
...
|
||||
|
||||
Let's add the following code to `index.html`:
|
||||
|
||||
__`app/index.html`:__
|
||||
<pre>
|
||||
<head>
|
||||
...
|
||||
<title>Google Phone Gallery</title>
|
||||
...
|
||||
</head>
|
||||
...
|
||||
<ul>
|
||||
<li>
|
||||
<span>Nexus S<span>
|
||||
<p>
|
||||
Fast just got faster with Nexus S.
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<span>Motorola XOOM™ with Wi-Fi<span>
|
||||
<p>
|
||||
The Next, Next Generation tablet.
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
...
|
||||
</pre>
|
||||
|
||||
## 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.
|
||||
|
||||
<table id="tutorial_nav">
|
||||
<tr>
|
||||
<td id="previous_step">{@link tutorial Previous}</td>
|
||||
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-1/app Example}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">
|
||||
{@link https://github.com/angular/angular-phonecat/commit/fa2a351f0ede1666041e407c52e4e5daf448c5f8
|
||||
Code Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_2 Next}</td>
|
||||
</tr>
|
||||
</table>
|
||||
109
docs/tutorial.step_10.ngdoc
Normal file
109
docs/tutorial.step_10.ngdoc
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Tutorial: Step 10
|
||||
@description
|
||||
<table id="tutorial_nav">
|
||||
<tr>
|
||||
<td id="previous_step">{@link tutorial.step_9 Previous}</td>
|
||||
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-10/app Example}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">{@link
|
||||
https://github.com/angular/angular-phonecat/commit/abe1e13c7d9e725fdd3b811ca5ec28ea0d973aab Code
|
||||
Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_11 Next}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
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`.__
|
||||
<pre>
|
||||
<img ng:src="{{mainImageUrl}}" class="phone"/>
|
||||
|
||||
<h1>{{phone.name}}</h1>
|
||||
|
||||
<p>{{phone.description}}</p>
|
||||
|
||||
<ul class="phone-thumbs">
|
||||
<li ng:repeat="img in phone.images">
|
||||
<img ng:src="{{img}}" ng:click="setImage(img)">
|
||||
</li>
|
||||
</ul>
|
||||
...
|
||||
</pre>
|
||||
|
||||
__`app/js/controllers.js`.__
|
||||
<pre>
|
||||
...
|
||||
function PhoneDetailCtrl($xhr) {
|
||||
var self = this;
|
||||
|
||||
$xhr('GET', 'phones/' + self.params.phoneId + '.json', function(code, response) {
|
||||
self.phone = response;
|
||||
self.mainImageUrl = response.images[0];
|
||||
});
|
||||
|
||||
self.setImage = function(imageUrl) {
|
||||
self.mainImageUrl = imageUrl;
|
||||
}
|
||||
}
|
||||
|
||||
//PhoneDetailCtrl.$inject = ['$xhr'];
|
||||
</pre>
|
||||
|
||||
__`test/e2e/scenarios.js`.__
|
||||
<pre>
|
||||
/* 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');
|
||||
});
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
|
||||
## 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.
|
||||
|
||||
|
||||
<table id="tutorial_nav">
|
||||
<tr>
|
||||
<td id="previous_step">{@link tutorial.step_9 Previous}</td>
|
||||
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-10/app Example}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">{@link
|
||||
https://github.com/angular/angular-phonecat/commit/abe1e13c7d9e725fdd3b811ca5ec28ea0d973aab Code
|
||||
Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_11 Next}</td>
|
||||
</tr>
|
||||
</table>
|
||||
113
docs/tutorial.step_11.ngdoc
Normal file
113
docs/tutorial.step_11.ngdoc
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Tutorial: Step 11
|
||||
@description
|
||||
<table id="tutorial_nav">
|
||||
<tr>
|
||||
<td id="previous_step">{@link tutorial.step_10 Previous}</td>
|
||||
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-11/app Example}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">{@link
|
||||
https://github.com/angular/angular-phonecat/commit/46e2bc3ff21a1385d6ef1860c5c242f8e0265379 Code
|
||||
Diff}</td>
|
||||
<td id="next_step">Next</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
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`.__
|
||||
<pre>
|
||||
...
|
||||
<script src="lib/angular/angular.js" ng:autobind></script>
|
||||
<script src="js/controllers.js"></script>
|
||||
<script src="js/filters.js"></script>
|
||||
<script src="js/services.js"></script>
|
||||
...
|
||||
</pre>
|
||||
|
||||
|
||||
__`app/js/services.js`.__ (New)
|
||||
<pre>
|
||||
angular.service('Phone', function($resource){
|
||||
return $resource('phones/:phoneId.json', {}, {
|
||||
query: {method:'GET', params:{phoneId:'phones'}, isArray:true}
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
|
||||
__`app/js/controllers.js`.__
|
||||
<pre>
|
||||
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'];
|
||||
|
||||
</pre>
|
||||
|
||||
|
||||
## 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.
|
||||
|
||||
<table id="tutorial_nav">
|
||||
<tr>
|
||||
<td id="previous_step">{@link tutorial.step_10 Previous}</td>
|
||||
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-11/app Example}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">{@link
|
||||
https://github.com/angular/angular-phonecat/commit/46e2bc3ff21a1385d6ef1860c5c242f8e0265379 Code
|
||||
Diff}</td>
|
||||
<td id="next_step">Next</td>
|
||||
</tr>
|
||||
</table>
|
||||
133
docs/tutorial.step_2.ngdoc
Executable file
133
docs/tutorial.step_2.ngdoc
Executable file
|
|
@ -0,0 +1,133 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Tutorial: Step 2
|
||||
@description
|
||||
<table id="tutorial_nav">
|
||||
<tr>
|
||||
<td id="previous_step">{@link tutorial.step_1 Previous}</td>
|
||||
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-2/app Example}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">{@link
|
||||
https://github.com/angular/angular-phonecat/commit/02e30dd64e0e5554fbf4d442ade5b1a251f2bf56
|
||||
Code Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_3 Next}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
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`:__
|
||||
<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>
|
||||
...
|
||||
</pre>
|
||||
|
||||
Our data __Model__ (a small set of phones in object literal notation) is instantiated within our
|
||||
__Controller__ function (`PhoneListCtrl`):
|
||||
|
||||
__`app/js/controllers.js`:__
|
||||
<pre>
|
||||
/* 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."}];
|
||||
}
|
||||
</pre>
|
||||
|
||||
The "Angular way" urges us to test as we develop:
|
||||
|
||||
__`test/unit/controllersSpec.js`:__
|
||||
<pre>
|
||||
/* 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);
|
||||
});
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
|
||||
## 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 `<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 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 `<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.
|
||||
|
||||
So, our controller function becomes the {@link angular.scope scope} of our data model.
|
||||
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.
|
||||
|
||||
* __Model:__ For our data model, we created a simple array of phone records, specified in object
|
||||
literal notation.
|
||||
|
||||
* __Testing:__ 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 using the technology baked into
|
||||
angular. The test verifies that we have some data, and that there are 3 records in the data set.
|
||||
|
||||
Angular's testing stack utilizes Jasmine's Behavior-driven Development (BDD) framework. You
|
||||
can learn about it on the {@link http://pivotal.github.com/jasmine/ Jasmine home page} and on
|
||||
the {@link https://github.com/pivotal/jasmine/wiki Jasmine wiki}.
|
||||
|
||||
<table id="tutorial_nav">
|
||||
<tr>
|
||||
<td id="previous_step">{@link tutorial.step_1 Previous}</td>
|
||||
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-2/app Example}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">{@link
|
||||
https://github.com/angular/angular-phonecat/commit/02e30dd64e0e5554fbf4d442ade5b1a251f2bf56
|
||||
Code Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_3 Next}</td>
|
||||
</tr>
|
||||
</table>
|
||||
104
docs/tutorial.step_3.ngdoc
Executable file
104
docs/tutorial.step_3.ngdoc
Executable file
|
|
@ -0,0 +1,104 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Tutorial: Step 3
|
||||
@description
|
||||
<table id="tutorial_nav">
|
||||
<tr>
|
||||
<td id="previous_step">{@link tutorial.step_2 Previous}</td>
|
||||
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-3/app Example}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">{@link
|
||||
https://github.com/angular/angular-phonecat/commit/a03815f8fb00217f5f9c1d3ef83282f79818e706 Code
|
||||
Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_4 Next}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
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`:__
|
||||
<pre>
|
||||
...
|
||||
Fulltext Search: <input name="query"/>
|
||||
|
||||
<ul class="phones">
|
||||
<li ng:repeat="phone in phones.$filter(query)">
|
||||
{{phone.name}}
|
||||
<p>{{phone.snippet}}</p>
|
||||
</li>
|
||||
</ul>
|
||||
...
|
||||
</pre>
|
||||
__`test/e2e/scenarios.js`:__
|
||||
<pre>
|
||||
/* 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);
|
||||
});
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
|
||||
## 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 `<input>` 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.
|
||||
|
||||
<table id="tutorial_nav">
|
||||
<tr>
|
||||
<td id="previous_step">{@link tutorial.step_2 Previous}</td>
|
||||
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-3/app Example}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">{@link
|
||||
https://github.com/angular/angular-phonecat/commit/a03815f8fb00217f5f9c1d3ef83282f79818e706 Code
|
||||
Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_4 Next}</td>
|
||||
</tr>
|
||||
</table>
|
||||
114
docs/tutorial.step_4.ngdoc
Executable file
114
docs/tutorial.step_4.ngdoc
Executable file
|
|
@ -0,0 +1,114 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Tutorial: Step 4
|
||||
@description
|
||||
<table id="tutorial_nav">
|
||||
<tr>
|
||||
<td id="previous_step">{@link tutorial.step_3 Previous}</td>
|
||||
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-4/app Example}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">{@link
|
||||
https://github.com/angular/angular-phonecat/commit/b56c91f453114347f0cb25e70b1c4fa4f1421763 Code
|
||||
Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_5 Next}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
In this step, we add a feature that lets our users choose which way to order the phone list.
|
||||
|
||||
__`app/index.html`:__
|
||||
<pre>
|
||||
...
|
||||
<ul class="predicates">
|
||||
<li>
|
||||
Search: <input type="text" name="query"/>
|
||||
</li>
|
||||
<li>
|
||||
Sort by:
|
||||
<select name="orderProp">
|
||||
<option value="name">Alphabetical</option>
|
||||
<option value="age">Newest</option>
|
||||
</select>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul class="phones">
|
||||
<li ng:repeat="phone in phones.$filter(query).$orderBy(orderProp)">
|
||||
{{phone.name}}
|
||||
<p>{{phone.snippet}}</p>
|
||||
</li>
|
||||
</ul>
|
||||
...
|
||||
</pre>
|
||||
|
||||
__`app/js/controller.js`:__
|
||||
<pre>
|
||||
/* 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';
|
||||
}
|
||||
</pre>
|
||||
|
||||
__`test/unit/controllerSpec.js`:__
|
||||
<pre>
|
||||
/* 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');
|
||||
});
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
|
||||
## 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.
|
||||
|
||||
<table id="tutorial_nav">
|
||||
<tr>
|
||||
<td id="previous_step">{@link tutorial.step_3 Previous}</td>
|
||||
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-4/app Example}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">{@link
|
||||
https://github.com/angular/angular-phonecat/commit/b56c91f453114347f0cb25e70b1c4fa4f1421763 Code
|
||||
Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_5 Next}</td>
|
||||
</tr>
|
||||
</table>
|
||||
109
docs/tutorial.step_5.ngdoc
Executable file
109
docs/tutorial.step_5.ngdoc
Executable file
|
|
@ -0,0 +1,109 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Tutorial: Step 5
|
||||
@description
|
||||
<table id="tutorial_nav">
|
||||
<tr>
|
||||
<td id="previous_step">{@link tutorial.step_4 Previous}</td>
|
||||
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-5/app Example}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">{@link
|
||||
https://github.com/angular/angular-phonecat/commit/4f0a518557b5c7442568666b211aa79499870276 Code
|
||||
Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_6 Next}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
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:`__
|
||||
<pre>
|
||||
/* 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'];
|
||||
</pre>
|
||||
|
||||
__`test/unit/controllerSpec.js`:__
|
||||
<pre>
|
||||
/* 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');
|
||||
});
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
|
||||
## 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.
|
||||
|
||||
<table id="tutorial_nav">
|
||||
<tr>
|
||||
<td id="previous_step">{@link tutorial.step_4 Previous}</td>
|
||||
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-5/app Example}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">{@link
|
||||
https://github.com/angular/angular-phonecat/commit/4f0a518557b5c7442568666b211aa79499870276 Code
|
||||
Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_6 Next}</td>
|
||||
</tr>
|
||||
</table>
|
||||
101
docs/tutorial.step_6.ngdoc
Executable file
101
docs/tutorial.step_6.ngdoc
Executable file
|
|
@ -0,0 +1,101 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Tutorial: Step 6
|
||||
@description
|
||||
<table id="tutorial_nav">
|
||||
<tr>
|
||||
<td id="previous_step">{@link tutorial.step_5 Previous}</td>
|
||||
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-6/app Example}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">{@link
|
||||
https://github.com/angular/angular-phonecat/commit/2fb113a4da9b6d19e17627f351f0681befcccdc0 Code
|
||||
Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_7 Next}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
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`:__
|
||||
<pre>
|
||||
...
|
||||
<ul class="predicates">
|
||||
<li>
|
||||
Search: <input type="text" name="query"/>
|
||||
</li>
|
||||
<li>
|
||||
Sort by:
|
||||
<select name="orderProp">
|
||||
<option value="name">Alphabetical</option>
|
||||
<option value="age">Newest</option>
|
||||
</select>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul class="phones">
|
||||
<li ng:repeat="phone in phones.$filter(query).$orderBy(orderProp)">
|
||||
<a href="#/phones/{{phone.id}}">{{phone.name}}</a>
|
||||
<a href="#/phones/{{phone.id}}" class="thumb"><img ng:src="{{phone.imageUrl}}"></a>
|
||||
<p>{{phone.snippet}}</p>
|
||||
</li>
|
||||
</ul>
|
||||
...
|
||||
</pre>
|
||||
|
||||
__`app/js/controller.js`__ (Unchanged):
|
||||
<pre>
|
||||
/* 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'];
|
||||
</pre>
|
||||
|
||||
__`app/phones/phones.json`__ (sample snippet):
|
||||
<pre>
|
||||
[
|
||||
{
|
||||
"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?"
|
||||
},
|
||||
…
|
||||
]
|
||||
</pre>
|
||||
|
||||
## 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 `<img>` 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.
|
||||
|
||||
<table id="tutorial_nav">
|
||||
<tr>
|
||||
<td id="previous_step">{@link tutorial.step_5 Previous}</td>
|
||||
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-6/app Example}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">{@link
|
||||
https://github.com/angular/angular-phonecat/commit/2fb113a4da9b6d19e17627f351f0681befcccdc0 Code
|
||||
Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_7 Next}</td>
|
||||
</tr>
|
||||
</table>
|
||||
159
docs/tutorial.step_7.ngdoc
Executable file
159
docs/tutorial.step_7.ngdoc
Executable file
|
|
@ -0,0 +1,159 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Tutorial: Step 7
|
||||
@description
|
||||
<table id="tutorial_nav">
|
||||
<tr>
|
||||
<td id="previous_step">{@link tutorial.step_6 Previous}</td>
|
||||
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-7/app Example}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">{@link
|
||||
https://github.com/angular/angular-phonecat/commit/43ff5d76f1c0a464da67d691418e33e6c9d8dbc8 Code
|
||||
Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_8 Next}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
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`:__
|
||||
<pre>
|
||||
<body ng:controller="PhoneCatCtrl">
|
||||
...
|
||||
|
||||
<ng:view></ng:view>
|
||||
...
|
||||
</pre>
|
||||
|
||||
__`app/partials/phone-list.html`:__
|
||||
<pre>
|
||||
<ul class="predicates">
|
||||
<li>
|
||||
Search: <input type="text" name="query"/>
|
||||
</li>
|
||||
<li>
|
||||
Sort by:
|
||||
<select name="orderProp">
|
||||
<option value="name">Alphabetical</option>
|
||||
<option value="age">Newest</option>
|
||||
</select>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul class="phones">
|
||||
<li ng:repeat="phone in phones.$filter(query).$orderBy(orderProp)">
|
||||
<a href="#/phones/{{phone.id}}">{{phone.name}}</a>
|
||||
<a href="#/phones/{{phone.id}}" class="thumb"><img ng:src="{{phone.imageUrl}}"></a>
|
||||
<p>{{phone.snippet}}</p>
|
||||
</li>
|
||||
</ul>
|
||||
</pre>
|
||||
|
||||
__`app/js/controller.js`:__
|
||||
<pre>
|
||||
/* 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() {}
|
||||
</pre>
|
||||
|
||||
## 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:
|
||||
`<ng:view></ng:view>`. 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.
|
||||
|
||||
<table id="tutorial_nav">
|
||||
<tr>
|
||||
<td id="previous_step">{@link tutorial.step_6 Previous}</td>
|
||||
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-7/app Example}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">{@link
|
||||
https://github.com/angular/angular-phonecat/commit/43ff5d76f1c0a464da67d691418e33e6c9d8dbc8 Code
|
||||
Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_8 Next}</td>
|
||||
</tr>
|
||||
</table>
|
||||
110
docs/tutorial.step_8.ngdoc
Executable file
110
docs/tutorial.step_8.ngdoc
Executable file
|
|
@ -0,0 +1,110 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Tutorial: Step 8
|
||||
@description
|
||||
<table id="tutorial_nav">
|
||||
<tr>
|
||||
<td id="previous_step">{@link tutorial.step_7 Previous}</td>
|
||||
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-8/app Example}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">{@link
|
||||
https://github.com/angular/angular-phonecat/commit/1f91f571bdd6f1e705ebb303998afe7820ffc6d9 Code
|
||||
Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_9 Next}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
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`:__
|
||||
<pre>
|
||||
<img ng:src="{{phone.images[0]}}" class="phone"/>
|
||||
|
||||
<h1>{{phone.name}}</h1>
|
||||
|
||||
<p>{{phone.description}}</p>
|
||||
|
||||
<ul class="phone-thumbs">
|
||||
<li ng:repeat="img in phone.images">
|
||||
<img ng:src="{{img}}"/>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul class="specs">
|
||||
<li>
|
||||
<span>Availability and Networks</span>
|
||||
<dl>
|
||||
<dt>Availability</dt>
|
||||
<dd ng:repeat="availability in phone.availability">{{availability}}</dd>
|
||||
</dl>
|
||||
</li>
|
||||
...
|
||||
<span>Additional Features</span>
|
||||
<dd>{{phone.additionalFeatures}}</dd>
|
||||
</li>
|
||||
</ul>
|
||||
</pre>
|
||||
|
||||
__`app/js/controller.js`:__
|
||||
<pre>
|
||||
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'];
|
||||
</pre>
|
||||
|
||||
__`app/phones/nexus-s.json`:__ (sample snippet)
|
||||
<pre>
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
## 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.
|
||||
|
||||
<table id="tutorial_nav">
|
||||
<tr>
|
||||
<td id="previous_step">{@link tutorial.step_7 Previous}</td>
|
||||
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-8/app Example}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">{@link
|
||||
https://github.com/angular/angular-phonecat/commit/1f91f571bdd6f1e705ebb303998afe7820ffc6d9 Code
|
||||
Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_9 Next}</td>
|
||||
</tr>
|
||||
</table>
|
||||
95
docs/tutorial.step_9.ngdoc
Executable file
95
docs/tutorial.step_9.ngdoc
Executable file
|
|
@ -0,0 +1,95 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Tutorial: Step 9
|
||||
@description
|
||||
<table id="tutorial_nav">
|
||||
<tr>
|
||||
<td id="previous_step">{@link tutorial.step_8 Previous}</td>
|
||||
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-9/app Example}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">{@link
|
||||
https://github.com/angular/angular-phonecat/commit/975d173ad0768487852387497c086f3c93fb48f6 Code
|
||||
Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_10 Next}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
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`.__
|
||||
<pre>
|
||||
...
|
||||
<script src="app/js/filters.js"></script>
|
||||
...
|
||||
</pre>
|
||||
|
||||
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`.__
|
||||
<pre>
|
||||
<img ng:src="{{phone.images[0].large}}" class="phone"/>
|
||||
<h1>{{phone.name}}</h1>
|
||||
<p>{{phone.description}}</p>
|
||||
...
|
||||
<ul class="specs">
|
||||
...
|
||||
<li>
|
||||
<span>Connectivity</span>
|
||||
<dl>
|
||||
<dt>Network Support</dt>
|
||||
<dd>{{phone.connectivity.cell}}</dd>
|
||||
<dt>WiFi</dt>
|
||||
<dd>{{phone.connectivity.wifi}}</dd>
|
||||
<dt>Bluetooth</dt>
|
||||
<dd>{{phone.connectivity.bluetooth}}</dd>
|
||||
<dt>Infrared</dt>
|
||||
<dd>{{phone.connectivity.infrared | checkmark}}</dd>
|
||||
<dt>GPS</dt>
|
||||
<dd>{{phone.connectivity.gps | checkmark}}</dd>
|
||||
</dl>
|
||||
</li>
|
||||
...
|
||||
</ul>
|
||||
</pre>
|
||||
|
||||
__`app/js/filters.js`.__ (New)
|
||||
<pre>
|
||||
/* http://docs.angularjs.org/#!angular.filter */
|
||||
|
||||
angular.filter('checkmark', function(input) {
|
||||
return input ? '\u2713' : '\u2718';
|
||||
});
|
||||
</pre>
|
||||
|
||||
|
||||
## 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`).
|
||||
|
||||
<table id="tutorial_nav">
|
||||
<tr>
|
||||
<td id="previous_step">{@link tutorial.step_8 Previous}</td>
|
||||
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-9/app Example}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">{@link
|
||||
https://github.com/angular/angular-phonecat/commit/975d173ad0768487852387497c086f3c93fb48f6 Code
|
||||
Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_10 Next}</td>
|
||||
</tr>
|
||||
</table>
|
||||
155
docs/tutorial_intro.ngdoc
Normal file
155
docs/tutorial_intro.ngdoc
Normal file
|
|
@ -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:
|
||||
|
||||
* <a href="#UsingGit">Using Git</a>. Use the Git Versioning System to get the files for each step.
|
||||
* <a href="#UsingSnapshots">Using Snapshots</a>. Download snapshots (files for each step of the
|
||||
tutorial) from the angular server.
|
||||
* <a href="#ReadingExamples">Reading the Examples</a>. 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.
|
||||
|
||||
<a name="PreReqs"></a>
|
||||
# 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.
|
||||
|
||||
<a name="UsingGit"></a>
|
||||
# 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 <a href="#PreReqs">prerequisites</a> 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".
|
||||
|
||||
<a name="UsingSnapshots"></a>
|
||||
# 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 <a href="#PreReqs">prerequisites</a> 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.
|
||||
|
||||
<a name="ReadingExamples"></a>
|
||||
# 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
|
||||
Loading…
Reference in a new issue