mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-17 07:40:22 +00:00
Update tutorial docs.
This commit is contained in:
parent
bd7e68f12f
commit
e205bd7137
14 changed files with 688 additions and 435 deletions
259
docs/tutorial.ngdoc
Executable file → Normal file
259
docs/tutorial.ngdoc
Executable file → Normal file
|
|
@ -1,87 +1,172 @@
|
|||
@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>
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Tutorial
|
||||
@description
|
||||
|
||||
A great way to get introduced to angular is to work through the {@link tutorial.step_0 angular
|
||||
tutorial}, which walks you through the construction of an angular web app. The app you will build
|
||||
in the tutorial is loosely based on the {@link http://www.google.com/phone/ Google phone gallery
|
||||
app}. The {@link http://angular.github.com/angular-phonecat/step-11/app/ end result of our effort}
|
||||
is visually simpler, but demonstrates many of the angular features without distractions in the
|
||||
form of CSS code.
|
||||
|
||||
This tutorial app ends up like a Google phone gallery app, but is originally based on the {@link
|
||||
https://github.com/angular/angular-seed angular-seed project}. The angular seed app isn't
|
||||
necessary for building angular apps, but it helps you get started quickly and makes the
|
||||
development and testing process much easier. Angular-seed includes a simple example, the latest
|
||||
angular libraries, test libraries, and scripts. It provides all of these in an environment that
|
||||
is pre-configured for developing a typical web app.
|
||||
|
||||
Once you set up your tutorial environment, you should be able to get through the material in less
|
||||
than a day and you'll have fun doing it. More experienced coders may be able to zip through the
|
||||
exercises in an afternoon. In any case, we promise that your time will be well spent!
|
||||
|
||||
When you finish the tutorial you will be able to:
|
||||
|
||||
* Create a simple dynamic application that works in any browser
|
||||
* Define the differences between angular and common JavaScript frameworks
|
||||
* Understand angular expressions
|
||||
* Understand how data binding works in angular
|
||||
* Use the angular-seed project to quickly boot-strap your own projects
|
||||
* Create and run tests
|
||||
* Identify resources for learning more about angular
|
||||
|
||||
You can work through the tutorial in any of the following ways:
|
||||
|
||||
* <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) and tinker with them.
|
||||
* <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:
|
||||
|
||||
* You need to be running on a Mac or Linux machine.
|
||||
* An http server running on your system. If you don't already have one installed, you can install
|
||||
`node.js` ({@link https://github.com/joyent/node/wiki/Installation node.js install}) or another
|
||||
http sever (such as Apache, etc.).
|
||||
* Java. This is required for running tests. Angular itself doesn't require Java.
|
||||
* A modern browser (including IE8+). Needed for viewing and debugging code.
|
||||
* A text editor of your choice.
|
||||
|
||||
<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.
|
||||
|
||||
2. Clone the angular-phonecat repository located at {@link
|
||||
https://github.com/angular/angular-phonecat angular-phonecat} by running the following command in
|
||||
a terminal:
|
||||
|
||||
git clone git://github.com/angular/angular-phonecat.git
|
||||
|
||||
This will create a directory called `angular-phonecat`.
|
||||
|
||||
3. In terminal, navigate to the `angular-phonecat` directory and run:
|
||||
|
||||
git checkout step-0
|
||||
|
||||
(You can run `git checkout step-[0-11]` to go to any of the steps in the tutorial).
|
||||
|
||||
4. To see the app running in a browser, do the following:
|
||||
* __For node.js users:__
|
||||
1. Run `./scripts/web-server.js` to start the app server.
|
||||
2. Open a browser window for the app and navigate to http://localhost:8000/app/index.html.
|
||||
|
||||
* __For other http servers:__
|
||||
1. Configure the server to serve the files in the `angular-phonecat` directory.
|
||||
2. Run `./scripts/web-server.js` to start the app server.
|
||||
3. Navigate in your browser to
|
||||
http://localhost:[*port-number*]/[*context-path*]/app/index.html.
|
||||
|
||||
5. To see tests running in a browser, do the following:
|
||||
* __For node.js users:__
|
||||
1. Run `./scripts/test-server.sh` to start the test web server.
|
||||
2. Open a browser window for the tests, navigate to http://localhost:9876, and choose
|
||||
"strict mode".
|
||||
* __For other http servers:__
|
||||
1. Configure the server to serve the files in the `angular-phonecat` directory.
|
||||
1. Run `./scripts/test-server.sh` to start the test web server.
|
||||
3. Navigate in your browser to http://localhost:[*port-number*]/, and choose "strict mode".
|
||||
|
||||
|
||||
|
||||
<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.
|
||||
|
||||
2. Navigate to [*the angular server*], and download and unzip [*the snapshot file*] to an
|
||||
[*install-dir*] of your choosing.
|
||||
|
||||
3. Change directories to [*install-dir*]/sandbox.
|
||||
|
||||
4. Run the following command:
|
||||
* `./goto_step.sh 0`
|
||||
|
||||
You have to start out at the beginning, which is Step 0. After you set up Step 0, you can skip
|
||||
around between any steps.
|
||||
|
||||
1. To see the app running in your browser, do the following:
|
||||
* __For node.js users:__
|
||||
1. Run `./scripts/web-server.js` to run the web server.
|
||||
2. Open a browser window for the app and navigate to http://localhost:8000/app/index.html.
|
||||
3. Open a browser window for the tests, navigate to http://localhost:9876, and choose
|
||||
"strict mode".
|
||||
|
||||
* __For other http servers:__
|
||||
1. Configure servers to serve the app and test files in the [*install-dir*]/sandbox.
|
||||
2. Start the server.
|
||||
3. Navigate in your app browser to
|
||||
http://localhost:[*port-number*]/[*context-path*]/app/index.html.
|
||||
4. Navigate in your test browser to http://localhost:[*port-number*] and choose "strict
|
||||
mode".
|
||||
|
||||
1. To view the tutorial app at different steps, run `./goto_step.sh [0-11]` and then refresh your
|
||||
browser. For example, say you're on Step 5 of the tutorial, and you want to see the app in action:
|
||||
|
||||
1. Run `goto_step.sh 5` from the command line in the `sandbox` directory.
|
||||
1. Refresh your app browser.
|
||||
|
||||
<a name="ReadingExamples"></a>
|
||||
# Reading the Examples
|
||||
|
||||
If you don't want to set up anything on your local machine, you can read through the tutorial and
|
||||
inspect the tutorial files on our servers; doing this will give you a good idea of what angular
|
||||
does, but you won't be able to make any code changes and experiment on your own.
|
||||
|
||||
To see the running app at each tutorial step, click the "Example" link at the top or bottom of
|
||||
each tutorial page.
|
||||
|
||||
To view the code differences between tutorial steps, click the Code Diff link at top or bottom of
|
||||
each tutorial page. Additions are highlighted in green; deletions are highlighted in red.
|
||||
|
||||
|
||||
# Relative URLs
|
||||
Throughout the tutorial, we use relative URLs to refer to files hosted on our local http server.
|
||||
The absolute URL depends on your configuration. For example, if you are using the node.js server,
|
||||
`app/index.html` translates to:
|
||||
|
||||
http://localhost:8000/app/index.html
|
||||
|
||||
If you are using your own http server running on port 8080 and the tutorial files are hosted at
|
||||
`/angular_tutorial`, `app/index.html` translates to:
|
||||
|
||||
http://localhost:8080/angular_tutorial/app/index.html
|
||||
|
|
|
|||
77
docs/tutorial.step_0.ngdoc
Executable file
77
docs/tutorial.step_0.ngdoc
Executable file
|
|
@ -0,0 +1,77 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Tutorial: Step 0
|
||||
@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-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>
|
||||
|
||||
The following sample code is our starting point. It is a static HTML page that displays next to
|
||||
nothing, but it has everything we need to proceed. You can think of this bit of code as our
|
||||
prototype template, consisting of basic HTML tags with a pair of angular specific attributes.
|
||||
|
||||
__`app/index.html`:__
|
||||
<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` script and
|
||||
registers a callback that will be executed by the browser when the containing HTML page 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 bootstrap and compile and manage the whole html 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 download the angular script from the angular server instead of from a local file.
|
||||
|
||||
* To try this code out in your browser, you need to navigate to the step-0 page (you are currently
|
||||
on Step 0 of the tutorial). If your http server is running, navigate to `app/index.html`.
|
||||
Remember, this is a relative URL (see the Relative URL section in {@link tutorial Tutorial}). The
|
||||
browser will display the same thing as you would see if you go to
|
||||
http://angular.github.com/angular-phonecat/step-0/app (accessible from Example link at the bottom
|
||||
of the page).
|
||||
|
||||
Now we can move on and add some content to our developing web app.
|
||||
|
||||
<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-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>
|
||||
|
|
@ -8,13 +8,13 @@
|
|||
<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>
|
||||
{@link https://github.com/angular/angular-phonecat/compare/step-0...step-1 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.
|
||||
Now that we have the basic ingredients in place, let's add some basic information about two cell
|
||||
phones to our app.
|
||||
|
||||
Note: We will usually include only the new code that we added for each step. In this and
|
||||
subsequent examples, we will leave out code from the previous step that hasn't changed, for
|
||||
|
|
@ -58,18 +58,21 @@ __`app/index.html`:__
|
|||
* 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:}__
|
||||
* __{@link tutorial Using Git:}__
|
||||
|
||||
From your `angular-phonecat` directory, run this command:
|
||||
|
||||
git checkout step-1
|
||||
|
||||
* __{@link tutorial_intro Using Snapshots:}__
|
||||
* __{@link tutorial Using Snapshots:}__
|
||||
|
||||
From `[install directory]/sandbox`, run this command:
|
||||
|
||||
./goto_step.sh 1
|
||||
|
||||
* Now would be a good time to open up `app/index.html` in your browser and see the current state
|
||||
of our "application". It's not very exciting, but that's ok.
|
||||
|
||||
When you're ready, let's move on and start using some angular features to turn this static page
|
||||
into a dynamic web app.
|
||||
|
||||
|
|
@ -79,8 +82,7 @@ into a dynamic web app.
|
|||
<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>
|
||||
{@link https://github.com/angular/angular-phonecat/compare/step-0...step-1 Code Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_2 Next}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
|
|
|||
|
|
@ -5,20 +5,20 @@
|
|||
<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="step_result">{@link http://angular.github.com/angular-phonecat/step-10/app Live Demo
|
||||
}</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="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-9...step-10
|
||||
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.
|
||||
The phone details view displays one large image of the current phone and several smaller thumbnail
|
||||
images. It would be great if we could replace the large image with any of the thumbnails just by
|
||||
clicking on the desired thumbnail image. Let's have a look how we can do this with angular.
|
||||
|
||||
__`app/partials/phone-detail.html`.__
|
||||
__`app/partials/phone-detail.html`:__
|
||||
<pre>
|
||||
<img ng:src="{{mainImageUrl}}" class="phone"/>
|
||||
|
||||
|
|
@ -34,7 +34,7 @@ __`app/partials/phone-detail.html`.__
|
|||
...
|
||||
</pre>
|
||||
|
||||
__`app/js/controllers.js`.__
|
||||
__`app/js/controllers.js`:__
|
||||
<pre>
|
||||
...
|
||||
function PhoneDetailCtrl($xhr) {
|
||||
|
|
@ -53,7 +53,7 @@ function PhoneDetailCtrl($xhr) {
|
|||
//PhoneDetailCtrl.$inject = ['$xhr'];
|
||||
</pre>
|
||||
|
||||
__`test/e2e/scenarios.js`.__
|
||||
__`test/e2e/scenarios.js`:__
|
||||
<pre>
|
||||
/* jasmine-like end2end tests go here */
|
||||
...
|
||||
|
|
@ -68,7 +68,7 @@ __`test/e2e/scenarios.js`.__
|
|||
expect(binding('phone.name')).toBe('Nexus S');
|
||||
});
|
||||
|
||||
it('should display "0.large" image as the main phone image', function() {
|
||||
it('should display the first phone image as the main phone image', function() {
|
||||
expect(element('img.phone').attr('src')).toBe('img/phones/nexus-s.0.jpg');
|
||||
});
|
||||
|
||||
|
|
@ -88,22 +88,23 @@ __`test/e2e/scenarios.js`.__
|
|||
|
||||
Adding the phone image swapping feature is fairly straightforward:
|
||||
|
||||
- We defined the `mainImageUrl` model variable in the details controller (`PhoneDetailCtrl`) and
|
||||
* We defined the `mainImageUrl` model property in the details controller (`PhoneDetailCtrl`) and
|
||||
set the default value of `mainImageUrl` to the first image in the array of images.
|
||||
- We created a `setImage` controller method to change `mainImageUrl` to the image clicked on by
|
||||
* 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.
|
||||
* We registered an `{@link angular.directive.ng:click ng:click}` handler for thumb images to use
|
||||
the `setImage` controller method.
|
||||
* We expanded the end-to-end test to verify that our new feature is swapping images correctly.
|
||||
|
||||
|
||||
<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="step_result">{@link http://angular.github.com/angular-phonecat/step-10/app Live Demo
|
||||
}</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="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-9...step-10
|
||||
Code Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_11 Next}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
|
|
|||
|
|
@ -5,26 +5,23 @@
|
|||
<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="step_result">{@link http://angular.github.com/angular-phonecat/step-11/app Live Demo
|
||||
}</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="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-10...step-11
|
||||
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.
|
||||
represents a {@link http://en.wikipedia.org/wiki/Representational_State_Transfer RESTful} client.
|
||||
Using this client we can make xhr requests for data in an easier way, without having to deal with
|
||||
the lower-level {@link angular.service.$xhr $xhr} APIs, HTTP methods and URLs.
|
||||
|
||||
__`app/index.html`.__
|
||||
<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>
|
||||
|
|
@ -41,34 +38,85 @@ __`app/js/services.js`.__ (New)
|
|||
|
||||
__`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) {
|
||||
function PhoneListCtrl(Phone_) {
|
||||
this.orderProp = 'age';
|
||||
this.phones = Phone.query();
|
||||
this.phones = Phone_.query();
|
||||
}
|
||||
//PhoneListCtrl.$inject = ['Phone'];
|
||||
|
||||
|
||||
function PhoneDetailCtrl(Phone) {
|
||||
this.phone = Phone.get({phoneId:this.params.phoneId});
|
||||
function PhoneDetailCtrl(Phone_) {
|
||||
this.phone = Phone_.get({phoneId:this.params.phoneId});
|
||||
}
|
||||
//PhoneDetailCtrl.$inject = ['Phone'];
|
||||
</pre>
|
||||
|
||||
__`test/unit/controllersSpec.js`:__
|
||||
<pre>
|
||||
/* jasmine specs for controllers go here */
|
||||
describe('PhoneCat controllers', function() {
|
||||
|
||||
beforeEach(function(){
|
||||
this.addMatchers({
|
||||
toEqualData: function(expected) {
|
||||
return angular.equals(this.actual, expected);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('PhoneListCtrl', function(){
|
||||
var scope, $browser, ctrl;
|
||||
|
||||
beforeEach(function() {
|
||||
scope = angular.scope();
|
||||
$browser = scope.$service('$browser');
|
||||
|
||||
$browser.xhr.expectGET('phones/phones.json').respond([{name: 'Nexus S'},
|
||||
{name: 'Motorola DROID'}]);
|
||||
ctrl = scope.$new(PhoneListCtrl);
|
||||
});
|
||||
|
||||
it('should create "phones" model with 2 phones fetched from xhr', function() {
|
||||
expect(ctrl.phones).toEqual([]);
|
||||
$browser.xhr.flush();
|
||||
|
||||
expect(ctrl.phones).toEqualData([{name: 'Nexus S'},
|
||||
{name: 'Motorola DROID'}]);
|
||||
});
|
||||
|
||||
it('should set the default value of orderProp model', function() {
|
||||
expect(ctrl.orderProp).toBe('age');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('PhoneDetailCtrl', function(){
|
||||
var scope, $browser, ctrl;
|
||||
|
||||
beforeEach(function() {
|
||||
scope = angular.scope();
|
||||
$browser = scope.$service('$browser');
|
||||
});
|
||||
|
||||
beforeEach(function() {
|
||||
scope = angular.scope();
|
||||
$browser = scope.$service('$browser');
|
||||
});
|
||||
|
||||
it('should fetch phone detail', function(){
|
||||
scope.params = {phoneId:'xyz'};
|
||||
$browser.xhr.expectGET('phones/xyz.json').respond({name:'phone xyz'});
|
||||
ctrl = scope.$new(PhoneDetailCtrl);
|
||||
|
||||
expect(ctrl.phone).toEqualData({});
|
||||
$browser.xhr.flush();
|
||||
|
||||
expect(ctrl.phone).toEqualData({name:'phone xyz'});
|
||||
});
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
|
||||
|
||||
|
|
@ -80,9 +128,26 @@ angular.service.$resource `$resource`} service is easier to use than `$xhr` for
|
|||
data sources exposed as RESTful resources. It is also easier now to understand what the code in
|
||||
our controllers is doing.
|
||||
|
||||
An important thing to notice in our controller code is that we don't pass any callback
|
||||
functions when invoking methods of our Phone services. It looks as if the result were returned
|
||||
synchronously. That is not the case at all. What is returned synchronously is a "future" — an
|
||||
object, which will be filled with data when the xhr response returns. Because of the
|
||||
data-binding in angular, we can use this future and bind it to our template. Then, when the
|
||||
data arrives, the view will automatically update. See? Angular tries hard to make simple
|
||||
stuff simple.
|
||||
|
||||
* Once again we make use of `$route's` params, this time to construct the URL passed as a
|
||||
parameter to `$resource` in our `services.js` script.
|
||||
|
||||
* Last, but certainly not least, we expanded and modified our unit test to verify that our new
|
||||
service is returning data as we expect it to.
|
||||
|
||||
In our assertions we use a newly-defined `toEqualData` {@link
|
||||
http://pivotal.github.com/jasmine/jsdoc/symbols/jasmine.Matchers.html Jasmine matcher}, which
|
||||
compares only object properties and ignores methods. This is necessary, because the `$resource`
|
||||
client will augment the response object with handy methods for updating and deleting the
|
||||
resource (we don't use these in our tutorial though).
|
||||
|
||||
There you have it! We have created a web app in a relatively short amount of time.
|
||||
|
||||
## Closing Notes:
|
||||
|
|
@ -103,11 +168,11 @@ 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="step_result">{@link http://angular.github.com/angular-phonecat/step-11/app Live Demo
|
||||
}</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="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-10...step-11
|
||||
Code Diff}</td>
|
||||
<td id="next_step">Next</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
|
|
|||
|
|
@ -7,17 +7,18 @@
|
|||
<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="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-1...step-2 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.
|
||||
In the last step, we remembered what a basic, static web page looks like, and now we want to get
|
||||
dynamic. There are many ways to do this, but an important feature of angular is the incorporation
|
||||
of the principles behind {@link http://en.wikipedia.org/wiki/Model–View–Controller the MVC design
|
||||
pattern} into client-side web apps. With that in mind, let's use a little angular and a little
|
||||
JavaScript to add Model, View, and Controller components to our app, and change the static page
|
||||
into one that is dynamically generated.
|
||||
|
||||
Our __View__ component is constructed by angular from this template:
|
||||
|
||||
|
|
@ -39,7 +40,7 @@ __`app/index.html`:__
|
|||
...
|
||||
</pre>
|
||||
|
||||
Our data __Model__ (a small set of phones in object literal notation) is instantiated within our
|
||||
Our data __Model__ (a short list of phones in object literal notation) is instantiated within our
|
||||
__Controller__ function (`PhoneListCtrl`):
|
||||
|
||||
__`app/js/controllers.js`:__
|
||||
|
|
@ -88,14 +89,15 @@ enclosed in curly braces: `{{phone.name}}` and `{{phone.snippet}}`:
|
|||
* 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
|
||||
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.
|
||||
through the HTML template. This means that whenever the model changes, angular refreshes the
|
||||
appropriate binding points, which updates the view.
|
||||
|
||||
* __Controller:__ At this point, it doesn't appear as if our controller is doing very much
|
||||
controlling, but it is playing a crucial role: providing context for our data model so we can
|
||||
establish 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:
|
||||
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
|
||||
|
|
@ -104,7 +106,6 @@ connected the dots between our presentation, data, and logic components:
|
|||
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.
|
||||
|
|
@ -114,20 +115,23 @@ 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. The test verifies that we have 3 records in the phones array.
|
||||
|
||||
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}.
|
||||
To run this test, make sure you have a {@link tutorial test server running}, and type
|
||||
`./scripts/test.sh` from the command line.
|
||||
|
||||
Angular developers prefer the syntax of Jasmine's Behavior-driven Development (BDD) framework
|
||||
when writing tests. So while Jasmine is not required by angular, we use 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}.
|
||||
|
||||
<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="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-1...step-2 Code
|
||||
Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_3 Next}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
|
|
|||
|
|
@ -14,9 +14,10 @@
|
|||
</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.
|
||||
We did a lot of work in laying the foundation of our app in the last step, so now we'll do
|
||||
something simple, and add full text search. We will also write an end-to-end test, because a good
|
||||
end-to-end test is a good friend. It stays with your app, keeps an eye on it, and quickly detects
|
||||
regressions.
|
||||
|
||||
__`app/index.html`:__
|
||||
<pre>
|
||||
|
|
@ -66,25 +67,28 @@ angular.Array.filter $filter} utility (added to the repeater) lets a user type i
|
|||
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.
|
||||
* Two way Data-binding. This is one of the core features in angular. When the page loads,
|
||||
angular binds the name of the input box to a variable of the same name in the data model and
|
||||
keeps the two in sync.
|
||||
|
||||
* 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.)
|
||||
In this example, the data that you type into the input box (named __`query`__) is immediately
|
||||
available as a filter input in the list repeater (`phone in phones.$filter(`__`query`__`)`).
|
||||
Whenever the data model changes and this change causes the input to the repeater to change, the
|
||||
repeater will efficiently update the DOM to reflect the current state of the model.
|
||||
|
||||
* How `ng:repeat` automatically shrinks and grows the number of phones in the View, via DOM
|
||||
* Use of `$filter` in a template. The `$filter` function is one of several built-in {@link
|
||||
angular.Array angular functions} that augment JavaScript arrays during their evaluation as
|
||||
angular expressions. In {@link guide.expression angular expressions}, these array utilities are
|
||||
available as array methods. (They are prefixed with a $ to avoid naming collisions.)
|
||||
|
||||
* `ng:repeat` automatically shrinks and grows the number of phones in the View, via DOM
|
||||
manipulation that is completely transparent to the developer. If you've written any DOM
|
||||
manipulation code, this should make you happy.
|
||||
|
||||
* __CSS:__ We added in some minimal CSS to the file we set up in Step 0: `./css/app.css`.
|
||||
|
||||
* __Testing:__ This end-to-end test shows the following:
|
||||
* __Testing:__ To run the end to end test, open http://localhost:8000/test/e2e/runner.html in
|
||||
your browser. This end-to-end test shows the following:
|
||||
|
||||
* Proof that the search box and the repeater are correctly wired together.
|
||||
|
||||
|
|
|
|||
|
|
@ -7,8 +7,7 @@
|
|||
<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
|
||||
<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-3...step-4 Code
|
||||
Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_5 Next}</td>
|
||||
</tr>
|
||||
|
|
@ -85,20 +84,69 @@ describe('PhoneCat controllers', function() {
|
|||
});
|
||||
</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);
|
||||
});
|
||||
|
||||
|
||||
it('should be possible to control phone order via the drop down select box', function() {
|
||||
input('query').enter('tablet'); //let's narrow the dataset to make the test assertions
|
||||
shorter
|
||||
|
||||
expect(repeater('.phones li', 'Phone List').column('a')).
|
||||
toEqual(["Motorola XOOM\u2122 with Wi-Fi",
|
||||
"MOTOROLA XOOM\u2122"]);
|
||||
|
||||
select('orderProp').option('alphabetical');
|
||||
|
||||
expect(repeater('.phones li', 'Phone List').column('a')).
|
||||
toEqual(["MOTOROLA XOOM\u2122",
|
||||
"Motorola XOOM\u2122 with Wi-Fi"]);
|
||||
});
|
||||
});
|
||||
});
|
||||
</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:
|
||||
|
||||
* First, we provide a `<select>` element named `orderProp` for our users so they can choose to
|
||||
sort the phone list either alphabetically or by the age of the phone. We added the `age` property
|
||||
to each phone record so we can sort by that field.
|
||||
|
||||
* 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`.
|
||||
* In our controller, we added a line to set the default value of `orderProp` to `age`. If we
|
||||
don't override the default value, angular uses the value of the first `<option>` element when it
|
||||
initializes the data model.
|
||||
|
||||
* Our unit test now verifies that our default ordering property is set.
|
||||
|
||||
* We added an end-to-end test to verify that our select box ordering mechanism works properly.
|
||||
|
||||
* Once again we added a little more CSS to improve the View.
|
||||
|
||||
<table id="tutorial_nav">
|
||||
|
|
@ -106,8 +154,7 @@ box that lets the user set the `orderProp` model variable to one of the string c
|
|||
<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
|
||||
<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-3...step-4 Code
|
||||
Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_5 Next}</td>
|
||||
</tr>
|
||||
|
|
|
|||
|
|
@ -7,17 +7,16 @@
|
|||
<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="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-4...step-5 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.
|
||||
`XMLHttpRequest` request to communicate with a server. Angular provides the built-in {@link
|
||||
angular.service.$xhr $xhr} service to make this easy.
|
||||
|
||||
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
|
||||
|
|
@ -83,27 +82,66 @@ to web apps. Angular provides several built-in services (such as {@link angular
|
|||
$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).
|
||||
as an argument to the controller's constructor function. The name of the argument is significant,
|
||||
because angular's {@link guide.di DI subsystem} recognizes the identity of a service by its name,
|
||||
and provides the name of the service to the controller during the controller's construction. The
|
||||
dependency injector also takes care of creating 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.
|
||||
Note: if you minify the javascript code for this controller, all function arguments will be
|
||||
minified as well. This will result in the dependency injector not being able to identify
|
||||
services correctly. To overcome this issue, just assign an array with service identifier strings
|
||||
into the `$inject` property of the controller function.
|
||||
|
||||
* __Testing:__ The unit test has been expanded. It now verifies that the `$xhr` service behaves
|
||||
as expected.
|
||||
* __`$xhr`:__ We moved our data set out of the controller and into the file
|
||||
`app/phones/phones.json` (and added some more phones). We used the `$xhr` service to make a GET
|
||||
HTTP request to our web server, asking for `phone/phones.json` (the url is relative to our
|
||||
`index.html` file). The server responds with the contents of the json file, which serves as the
|
||||
source of our data. Keep in mind that the response might just as well have been dynamically
|
||||
generated by a sophisticated backend server. To our web server they both look the same, but using
|
||||
a real backend server to generate a response would make our tutorial unnecessarily complicated.
|
||||
|
||||
Notice that the $xhr service takes a callback as the last parameter. This callback is used to
|
||||
process the response. In our case, we just assign the response to the current scope controlled
|
||||
by the controller, as a model called `phones`. Have you realized that we didn't even have to
|
||||
parse the response? Angular took care of that for us.
|
||||
|
||||
* __Testing:__ The unit tests have been expanded. Because of the dependency injection business,
|
||||
we now need to create the controller the same way that angular does it behind the scenes. For this
|
||||
reason, we need to:
|
||||
|
||||
* Create a root scope object by calling `angular.scope()`
|
||||
|
||||
* Call `scope.$new(PhoneListCtrl)` to get angular to create the child scope associated with
|
||||
our controller.
|
||||
|
||||
At the same time, we need to tell the testing harness that it should expect an incoming
|
||||
request from our controller. To do this we:
|
||||
|
||||
* Use the `$service` method to retrieve the `$browser` service - this is a service that in
|
||||
angular represents various browser APIs. In tests, angular automatically uses a mock version
|
||||
of this service that allows you to write tests without having to deal with these native APIs
|
||||
and the global state associated with them.
|
||||
|
||||
* We use the `$browser.expectGET` method to train the `$browser` object to expect an incoming
|
||||
http request and tell it what to respond with. Note that the responses are not returned before
|
||||
we call the `$browser.xhr.flush()` method.
|
||||
|
||||
* We then make assertions to verify that the `phones` model doesn't exist on the scope, before
|
||||
the response is received.
|
||||
|
||||
* We flush the xhr queue in the browser by calling `$browser.xhr.flush()`. This causes the
|
||||
callback we passed into the `$xhr` service to be executed with the trained response.
|
||||
|
||||
* Finally, we make the assertions, verifying that the phone model now exists on the scope.
|
||||
|
||||
<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="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-4...step-5
|
||||
Code Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_6 Next}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
|
|
|||
|
|
@ -7,8 +7,7 @@
|
|||
<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
|
||||
<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-5...step-6 Code
|
||||
Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_7 Next}</td>
|
||||
</tr>
|
||||
|
|
@ -77,6 +76,17 @@ __`app/phones/phones.json`__ (sample snippet):
|
|||
]
|
||||
</pre>
|
||||
|
||||
__`test/e2e/scenarios.js`__:
|
||||
<pre>
|
||||
...
|
||||
it('should render phone specific links', function() {
|
||||
input('query').enter('nexus');
|
||||
element('.phones li a').click();
|
||||
expect(browser().location().hash()).toBe('/phones/nexus-s');
|
||||
});
|
||||
...
|
||||
</pre>
|
||||
|
||||
## Discussion:
|
||||
|
||||
* Note that we're using {@link guide.expression angular expressions} enclosed in the now-familiar
|
||||
|
|
@ -88,13 +98,15 @@ That directive prevents the browser from treating the angular `{{ exppression }}
|
|||
literally, as it would do if we tried to use markup in a regular `src` attribute. Use `ng:src` to
|
||||
keep the browser from eagerly making an extra http request to an invalid location.
|
||||
|
||||
* We expanded our end-to-end test to verify that the app is generating correct links to the phone
|
||||
views we will implement in the upcoming steps.
|
||||
|
||||
<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
|
||||
<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-5...step-6 Code
|
||||
Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_7 Next}</td>
|
||||
</tr>
|
||||
|
|
|
|||
|
|
@ -5,40 +5,60 @@
|
|||
<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="step_result">{@link http://angular.github.com/angular-phonecat/step-7/app Live Demo
|
||||
}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">{@link
|
||||
https://github.com/angular/angular-phonecat/commit/43ff5d76f1c0a464da67d691418e33e6c9d8dbc8 Code
|
||||
<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-6...step-7 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
|
||||
Our app is slowly growing and becoming more complex. Up until now, the app provided our users with
|
||||
just one view (the list of all phones), and all of our template code was located in the
|
||||
`index.html` file. The next step in building our app is the addition of a view that will show
|
||||
detailed information about each of the devices in our list.
|
||||
|
||||
To add the detailed view, we could expand the `index.html` file to contain template code for both
|
||||
views, but that would get messy very quickly. Instead, we are going to turn the `index.html`
|
||||
template into what we call a "layout template". This is a template that is common for all views in
|
||||
our application. Other "partial templates" are then included into this layout template depending
|
||||
on the current "route" — the view that is currently displayed to the user.
|
||||
|
||||
Similarly as with templates, angular also allows for controllers and scopes managed by these
|
||||
controllers to be nested. We are going to create a "root" controller called `PhoneCatCtrl`, which
|
||||
will contain the declaration of routes for the application.
|
||||
|
||||
Application routes in angular are declared via the {@link angular.service.$route $route} service.
|
||||
This services makes it easy to wire together controllers, View templates, and the current URL
|
||||
location in the browser. Using this feature we can implement {@link
|
||||
http://en.wikipedia.org/wiki/Deep_linking deep linking}, which lets us utilize the browser's
|
||||
History, and Back and Forward browser navigation.
|
||||
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.
|
||||
We'll use the $route service to declare that our application consists of two different views: one
|
||||
view presents the phone listing, and the other view presents the details for a particular phone.
|
||||
Each view will have the template stored in a separate file in the `app/partials/` directory.
|
||||
Similarly each view will have a controller associated with it. These will be stored in the
|
||||
existing `app/js/controllers.js` file.
|
||||
|
||||
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.)
|
||||
The `$route` service is usually used in conjunction with the {@link angular.widget.ng:view
|
||||
ng:view} widget. The role of the `ng:view` widget is to include the view template for the current
|
||||
route into the layout template, which makes it a perfect fit for our `index.html` template.
|
||||
|
||||
For now we are going to get all the routing going, and move the phone listing template into a
|
||||
separate file. We'll save the implementation of the phone details View for the next step.
|
||||
|
||||
__`app/index.html`:__
|
||||
<pre>
|
||||
<body ng:controller="PhoneCatCtrl">
|
||||
...
|
||||
<body ng:controller="PhoneCatCtrl">
|
||||
|
||||
<ng:view></ng:view>
|
||||
...
|
||||
|
||||
<script src="lib/angular/angular.js" ng:autobind></script>
|
||||
<script src="js/controllers.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
</pre>
|
||||
|
||||
__`app/partials/phone-list.html`:__
|
||||
|
|
@ -65,6 +85,11 @@ __`app/partials/phone-list.html`:__
|
|||
</ul>
|
||||
</pre>
|
||||
|
||||
__`app/partials/phone-list.html`:__
|
||||
<pre>
|
||||
TBD: detail view for {{params.phoneId}}
|
||||
</pre>
|
||||
|
||||
__`app/js/controller.js`:__
|
||||
<pre>
|
||||
/* App Controllers */
|
||||
|
|
@ -106,53 +131,50 @@ 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:
|
||||
`<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).
|
||||
`<ng:view></ng:view>`. As described above, it is now a "layout template". We added the following
|
||||
two new View templates:
|
||||
|
||||
* We added two new View templates:
|
||||
* `app/partials/phone-list.html` for the phone list. The phone-list view was formerly our
|
||||
main view. We simply moved the code from `index.html` to here.
|
||||
|
||||
* `app/partials/phone-list.html` for the phone list;
|
||||
|
||||
* `app/partials/phone-detail.html` for the phone details (just a stub for this step);
|
||||
* `app/partials/phone-detail.html` for the phone details (just a placeholder template for now).
|
||||
|
||||
* __The Controller(s).__ We now have a new root controller (`PhoneCatCtrl`) and two
|
||||
sub-controllers (`PhoneListCtrl` and `PhoneDetailCtrl`).
|
||||
sub-controllers (`PhoneListCtrl` and `PhoneDetailCtrl`). These inherit the model properties and
|
||||
behavior from the root controller.
|
||||
|
||||
* __`$route.`__ The root controller's job now is to set up the `$route` configuration:
|
||||
|
||||
* When the fragment part of the URL in the browser ends in "/phones", `$route` grabs the
|
||||
`phone-list.html` template, compiles it, and links it with a new scope that is controlled
|
||||
by our `PhoneListCtrl` controller.
|
||||
* When the fragment part of the URL in the browser ends in "/phones", `$route` service
|
||||
grabs the `phone-list.html` template, compiles it, and links it with a new scope that is
|
||||
controlled by our `PhoneListCtrl` controller.
|
||||
|
||||
* When the URL ends in "/phones/:phoneId", `$route` compiles and links the
|
||||
`phone-detail.html` template as it did with `phone-list.html`. But note the use of the
|
||||
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.
|
||||
`:phoneId` parameter declaration in the `path` argument of `$route.when()`: `$route`
|
||||
services provides all the values for variables defined in this way as
|
||||
`$route.current.params` map. In our route, `$route.current.params.phoneId` always holds
|
||||
the current contents of the `:phoneId` portion of the URL. We will use the `phoneId`
|
||||
parameter when we fetch the phone details in Step 8.
|
||||
|
||||
* Any other URL fragment gets redirected to `/phones`.
|
||||
|
||||
* __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.
|
||||
* __Controller/Scope inheritance.__ In the function passed into `$route`'s `onChange()`
|
||||
method, we copied url parameters extracted from the current route to the `params` property in
|
||||
the root scope. This property is inherited by child scopes created for our view controllers
|
||||
and accessible by these controllers.
|
||||
|
||||
* __Tests.__ To automatically verify that everything is wired properly, we write end to end
|
||||
tests that navigate to various URLs and verify that the correct view was rendered.
|
||||
|
||||
<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="step_result">{@link http://angular.github.com/angular-phonecat/step-7/app Live Demo
|
||||
}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">{@link
|
||||
https://github.com/angular/angular-phonecat/commit/43ff5d76f1c0a464da67d691418e33e6c9d8dbc8 Code
|
||||
<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-6...step-7 Code
|
||||
Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_8 Next}</td>
|
||||
</tr>
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@
|
|||
<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="step_result">{@link http://angular.github.com/angular-phonecat/step-8/app Live Demo
|
||||
}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">{@link
|
||||
https://github.com/angular/angular-phonecat/commit/1f91f571bdd6f1e705ebb303998afe7820ffc6d9 Code
|
||||
<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-7...step-8 Code
|
||||
Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_9 Next}</td>
|
||||
</tr>
|
||||
|
|
@ -41,6 +41,7 @@ __`app/partials/phone-details.html`:__
|
|||
</dl>
|
||||
</li>
|
||||
...
|
||||
</li>
|
||||
<span>Additional Features</span>
|
||||
<dd>{{phone.additionalFeatures}}</dd>
|
||||
</li>
|
||||
|
|
@ -87,23 +88,60 @@ __`app/phones/nexus-s.json`:__ (sample snippet)
|
|||
}
|
||||
</pre>
|
||||
|
||||
__`test/unit/controllerSpec.js`:__
|
||||
<pre>
|
||||
...
|
||||
it('should fetch phone detail', function(){
|
||||
scope.params = {phoneId:'xyz'};
|
||||
$browser.xhr.expectGET('phones/xyz.json').respond({name:'phone xyz'});
|
||||
ctrl = scope.$new(PhoneDetailCtrl);
|
||||
|
||||
expect(ctrl.phone).toBeUndefined();
|
||||
$browser.xhr.flush();
|
||||
|
||||
expect(ctrl.phone).toEqual({name:'phone xyz'});
|
||||
});
|
||||
...
|
||||
</pre>
|
||||
|
||||
__`test/e2e/scenarios.js`:__
|
||||
<pre>
|
||||
...
|
||||
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');
|
||||
});
|
||||
});
|
||||
...
|
||||
</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.
|
||||
angular `{{ expression }}` markup and directives to project phone data from our model into the
|
||||
view.
|
||||
|
||||
* Note how we used the `$route` `params` object from the scope managed by the root controller
|
||||
(`PhoneCatCtrl`), to construct the path 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.
|
||||
(`PhoneCatCtrl`), to construct the path for the phone details xhr request. The rest of this step
|
||||
is simply applying the previously learned concepts and angular APIs to create a large template
|
||||
that displays a lot of data about a phone.
|
||||
|
||||
* Tests. We updated the existing end to end test and wrote a new unit test that is similar in
|
||||
spirit to the one we wrote for the `PhoneListCtrl` controller.
|
||||
|
||||
<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="step_result">{@link http://angular.github.com/angular-phonecat/step-8/app Live Demo
|
||||
}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">{@link
|
||||
https://github.com/angular/angular-phonecat/commit/1f91f571bdd6f1e705ebb303998afe7820ffc6d9 Code
|
||||
<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-7...step-8 Code
|
||||
Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_9 Next}</td>
|
||||
</tr>
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@
|
|||
<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="step_result">{@link http://angular.github.com/angular-phonecat/step-9/app Live Demo
|
||||
}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">{@link
|
||||
https://github.com/angular/angular-phonecat/commit/975d173ad0768487852387497c086f3c93fb48f6 Code
|
||||
<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-8...step-9 Code
|
||||
Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_10 Next}</td>
|
||||
</tr>
|
||||
|
|
@ -16,18 +16,20 @@ Diff}</td>
|
|||
|
||||
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
|
||||
angular.filter.date date}, etc.) don't handle what we want to do, so we get to create our own
|
||||
custom {@link angular.filter filter}.
|
||||
|
||||
In the previous step, the details page displayed either "true" or "false" to indicate whether
|
||||
certain phone features were present or not. Our custom "checkmark" filter replaces those text
|
||||
strings with images: ✓ for "true", and ✘ for "false".
|
||||
strings with glyphs: ✓ for "true", and ✘ for "false".
|
||||
|
||||
Our filter code lives in `app/js/filters.js`:
|
||||
|
||||
__`app/index.html`.__
|
||||
__`app/index.html`:__
|
||||
<pre>
|
||||
...
|
||||
<script src="lib/angular/angular.js" ng:autobind></script>
|
||||
<script src="js/controllers.js"></script>
|
||||
<script src="app/js/filters.js"></script>
|
||||
...
|
||||
</pre>
|
||||
|
|
@ -35,7 +37,7 @@ __`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`.__
|
||||
__`app/partials/phone-detail.html`:__
|
||||
<pre>
|
||||
<img ng:src="{{phone.images[0].large}}" class="phone"/>
|
||||
<h1>{{phone.name}}</h1>
|
||||
|
|
@ -62,33 +64,44 @@ __`app/partials/phone-detail.html`.__
|
|||
</ul>
|
||||
</pre>
|
||||
|
||||
__`app/js/filters.js`.__ (New)
|
||||
__`app/js/filters.js`:__ (New)
|
||||
<pre>
|
||||
/* http://docs.angularjs.org/#!angular.filter */
|
||||
|
||||
angular.filter('checkmark', function(input) {
|
||||
return input ? '\u2713' : '\u2718';
|
||||
});
|
||||
</pre>
|
||||
|
||||
__`test/unit/filtersSpec.js`:__ (New)
|
||||
<pre>
|
||||
describe('checkmark filter', function() {
|
||||
|
||||
it('should convert boolean values to unicode checkmark or cross', function() {
|
||||
expect(angular.filter.checkmark(true)).toBe('\u2713');
|
||||
expect(angular.filter.checkmark(false)).toBe('\u2718');
|
||||
});
|
||||
})
|
||||
</pre>
|
||||
|
||||
## Discussion:
|
||||
|
||||
This example shows how easy it is to roll your own filters for displaying data. As explained in
|
||||
* 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.
|
||||
simply register your custom filter function on to the `angular.filter` function.
|
||||
|
||||
In this example, our filter name is "checkmark"; our input is either "true" or "false", and we
|
||||
* In this example, our filter name is "checkmark"; our input is either "true" or "false", and we
|
||||
return one of two unicode characters we have chosen to represent true or false (`\u2713` and
|
||||
`\u2718`).
|
||||
|
||||
* We created a new unit test to verify that our custom filter converts boolean values to unicode
|
||||
characters.
|
||||
|
||||
<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="step_result">{@link http://angular.github.com/angular-phonecat/step-9/app Live Demo
|
||||
}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">{@link
|
||||
https://github.com/angular/angular-phonecat/commit/975d173ad0768487852387497c086f3c93fb48f6 Code
|
||||
<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-8...step-9 Code
|
||||
Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_10 Next}</td>
|
||||
</tr>
|
||||
|
|
|
|||
|
|
@ -1,155 +0,0 @@
|
|||
@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