docs - stripping extra new lines

This commit is contained in:
Igor Minar 2011-06-15 22:31:40 -07:00
parent d428c9910e
commit b842642b57
75 changed files with 3 additions and 1723 deletions

View file

@ -3,11 +3,9 @@
@name angular.service @name angular.service
@description @description
The services API provides objects for carrying out common web app tasks. Service objects are The services API provides objects for carrying out common web app tasks. Service objects are
managed by angular's {@link guide/dev_guide.di dependency injection system}. managed by angular's {@link guide/dev_guide.di dependency injection system}.
* {@link angular.service.$browser $browser } - Provides an instance of a browser object * {@link angular.service.$browser $browser } - Provides an instance of a browser object
* {@link angular.service.$cookieStore $cookieStore } - Provides key / value storage backed by * {@link angular.service.$cookieStore $cookieStore } - Provides key / value storage backed by
session cookies session cookies
@ -27,6 +25,5 @@ server-side data sources
* {@link angular.service.$window $window } - References the browsers `window` object * {@link angular.service.$window $window } - References the browsers `window` object
* {@link angular.service.$xhr $xhr} - Generates an XHR request. * {@link angular.service.$xhr $xhr} - Generates an XHR request.
For information on how angular services work and how to write your own services, see {@link For information on how angular services work and how to write your own services, see {@link
guide/dev_guide.services Angular Services} in the angular Developer Guide. guide/dev_guide.services Angular Services} in the angular Developer Guide.

View file

@ -2,10 +2,8 @@
@name API Reference @name API Reference
@description @description
## Angular Compiler API ## Angular Compiler API
* {@link angular.widget Widgets} - Angular custom DOM element * {@link angular.widget Widgets} - Angular custom DOM element
* {@link angular.directive Directives} - Angular DOM element attributes * {@link angular.directive Directives} - Angular DOM element attributes
* {@link angular.markup Markup} and {@link angular.attrMarkup Attribute Markup} * {@link angular.markup Markup} and {@link angular.attrMarkup Attribute Markup}
@ -14,47 +12,33 @@
* {@link angular.validator Validators} - Angular input validators * {@link angular.validator Validators} - Angular input validators
* {@link angular.compile angular.compile()} - Template compiler * {@link angular.compile angular.compile()} - Template compiler
## Angular Scope API ## Angular Scope API
* {@link angular.scope Scope Object} - Angular scope object * {@link angular.scope Scope Object} - Angular scope object
## Angular Services & Dependency Injection API ## Angular Services & Dependency Injection API
* {@link angular.service Angular Services} * {@link angular.service Angular Services}
* {@link angular.injector angular.injector() } * {@link angular.injector angular.injector() }
## Angular Testing API ## Angular Testing API
* {@link angular.mock Testing Mocks API} - Mock objects for testing * {@link angular.mock Testing Mocks API} - Mock objects for testing
* {@link * {@link
https://docs.google.com/document/d/11L8htLKrh6c92foV71ytYpiKkeKpM4_a5-9c3HywfIc/edit?hl=en_US https://docs.google.com/document/d/11L8htLKrh6c92foV71ytYpiKkeKpM4_a5-9c3HywfIc/edit?hl=en_US
Angular Scenario Runner} - Automated scenario testing documentation Angular Scenario Runner} - Automated scenario testing documentation
## Angular Utility Functions ## Angular Utility Functions
### HTML & DOM Manipulation ### HTML & DOM Manipulation
* {@link angular.element angular.element()} * {@link angular.element angular.element()}
### Misc ### Misc
* {@link angular.bind angular.bind() } * {@link angular.bind angular.bind() }
* {@link angular.extend angular.extend() } * {@link angular.extend angular.extend() }
* {@link angular.forEach angular.forEach() } * {@link angular.forEach angular.forEach() }
@ -62,11 +46,8 @@ Angular Scenario Runner} - Automated scenario testing documentation
* {@link angular.noop angular.noop() } * {@link angular.noop angular.noop() }
## Type Identification ## Type Identification
* {@link angular.isArray angular.isArray() } * {@link angular.isArray angular.isArray() }
* {@link angular.isDate angular.isDate() } * {@link angular.isDate angular.isDate() }
* {@link angular.isDefined angular.isDefined() } * {@link angular.isDefined angular.isDefined() }
@ -76,25 +57,18 @@ Angular Scenario Runner} - Automated scenario testing documentation
* {@link angular.isString angular.isString() } * {@link angular.isString angular.isString() }
* {@link angular.isUndefined angular.isUndefined() } * {@link angular.isUndefined angular.isUndefined() }
## Strings ## Strings
* {@link angular.lowercase angular.lowercase() } * {@link angular.lowercase angular.lowercase() }
* {@link angular.uppercase angular.uppercase() } * {@link angular.uppercase angular.uppercase() }
### JSON ### JSON
* {@link angular.fromJson angular.fromJson() } * {@link angular.fromJson angular.fromJson() }
* {@link angular.toJson angular.toJson() } * {@link angular.toJson angular.toJson() }
## Utility methods for JavaScript types ## Utility methods for JavaScript types
* {@link angular.Object Object API} - Utility functions for JavaScript objects * {@link angular.Object Object API} - Utility functions for JavaScript objects
* {@link angular.Array Array API} - Utility functions for JavaScript arrays * {@link angular.Array Array API} - Utility functions for JavaScript arrays

View file

@ -3,11 +3,9 @@
@name Cookbook: Advanced Form @name Cookbook: Advanced Form
@description @description
Here we extend the basic form example to include common features such as reverting, dirty state Here we extend the basic form example to include common features such as reverting, dirty state
detection, and preventing invalid form submission. detection, and preventing invalid form submission.
<doc:example> <doc:example>
<doc:source> <doc:source>
<script> <script>
@ -31,13 +29,11 @@ detection, and preventing invalid form submission.
this.cancel(); this.cancel();
} }
UserForm.prototype = { UserForm.prototype = {
cancel: function(){ cancel: function(){
this.form = angular.copy(this.master); this.form = angular.copy(this.master);
}, },
save: function(){ save: function(){
this.master = this.form; this.master = this.form;
this.cancel(); this.cancel();
@ -46,11 +42,9 @@ detection, and preventing invalid form submission.
</script> </script>
<div ng:controller="UserForm"> <div ng:controller="UserForm">
<label>Name:</label><br/> <label>Name:</label><br/>
<input type="text" name="form.name" ng:required/> <br/><br/> <input type="text" name="form.name" ng:required/> <br/><br/>
<label>Address:</label><br/> <label>Address:</label><br/>
<input type="text" name="form.address.line1" size="33" ng:required/> <br/> <input type="text" name="form.address.line1" size="33" ng:required/> <br/>
<input type="text" name="form.address.city" size="12" ng:required/>, <input type="text" name="form.address.city" size="12" ng:required/>,
@ -58,7 +52,6 @@ detection, and preventing invalid form submission.
<input type="text" name="form.address.zip" size="5" ng:required <input type="text" name="form.address.zip" size="5" ng:required
ng:validate="regexp:zip"/><br/><br/> ng:validate="regexp:zip"/><br/><br/>
<label>Contacts:</label> <label>Contacts:</label>
[ <a href="" ng:click="form.contacts.$add()">add</a> ] [ <a href="" ng:click="form.contacts.$add()">add</a> ]
<div ng:repeat="contact in form.contacts"> <div ng:repeat="contact in form.contacts">
@ -75,7 +68,6 @@ ng:validate="regexp:zip"/><br/><br/>
<button ng:click="save()" ng:disabled="{{$invalidWidgets.visible() || <button ng:click="save()" ng:disabled="{{$invalidWidgets.visible() ||
master.$equals(form)}}">Save</button> master.$equals(form)}}">Save</button>
<hr/> <hr/>
Debug View: Debug View:
<pre>form={{form}} <pre>form={{form}}
@ -104,11 +96,8 @@ master.$equals(form)}}">Save</button>
</doc:example> </doc:example>
#Things to notice #Things to notice
* Cancel & save buttons are only enabled if the form is dirty — there is something to cancel or * Cancel & save buttons are only enabled if the form is dirty — there is something to cancel or
save. save.
* Save button is only enabled if there are no validation errors on the form. * Save button is only enabled if there are no validation errors on the form.

View file

@ -3,7 +3,6 @@
@name Cookbook: Resources - Buzz @name Cookbook: Resources - Buzz
@description @description
External resources are URLs that provide JSON data, which are then rendered with the help of External resources are URLs that provide JSON data, which are then rendered with the help of
templates. angular has a resource factory that can be used to give names to the URLs and then templates. angular has a resource factory that can be used to give names to the URLs and then
attach behavior to them. For example you can use the attach behavior to them. For example you can use the
@ -11,7 +10,6 @@ attach behavior to them. For example you can use the
API} API}
to retrieve Buzz activity and comments. to retrieve Buzz activity and comments.
<doc:example> <doc:example>
<doc:source> <doc:source>
<script> <script>

View file

@ -3,18 +3,14 @@
@name Cookbook: Deep Linking @name Cookbook: Deep Linking
@description @description
Deep linking allows you to encode the state of the application in the URL so that it can be Deep linking allows you to encode the state of the application in the URL so that it can be
bookmarked and the application can be restored from the URL to the same state. bookmarked and the application can be restored from the URL to the same state.
While <angular/> does not force you to deal with bookmarks in any particular way, it has services While <angular/> does not force you to deal with bookmarks in any particular way, it has services
which make the common case described here very easy to implement. which make the common case described here very easy to implement.
# Assumptions # Assumptions
Your application consists of a single HTML page which bootstraps the application. We will refer Your application consists of a single HTML page which bootstraps the application. We will refer
to this page as the chrome. to this page as the chrome.
Your application is divided into several screens (or views) which the user can visit. For example, Your application is divided into several screens (or views) which the user can visit. For example,
@ -25,30 +21,22 @@ screen will be constructed from an HTML snippet, which we will refer to as the p
have multiple partials, but a single partial is the most common construct. This example makes the have multiple partials, but a single partial is the most common construct. This example makes the
partial boundary visible using a blue line. partial boundary visible using a blue line.
You can make a routing table which shows which URL maps to which partial view template and which You can make a routing table which shows which URL maps to which partial view template and which
controller. controller.
# Example # Example
In this example we have a simple app which consist of two screens: In this example we have a simple app which consist of two screens:
* Welcome: url `#` Show the user contact information. * Welcome: url `#` Show the user contact information.
* Settings: url `#/settings` Show an edit screen for user contact information. * Settings: url `#/settings` Show an edit screen for user contact information.
The two partials are defined in the following URLs: The two partials are defined in the following URLs:
* {@link ./examples/settings.html} * {@link ./examples/settings.html}
* {@link ./examples/welcome.html} * {@link ./examples/welcome.html}
<doc:example> <doc:example>
<doc:source> <doc:source>
<script> <script>
@ -59,7 +47,6 @@ The two partials are defined in the following URLs:
$route.when("/settings", {template:'./examples/settings.html', controller:SettingsCntl}); $route.when("/settings", {template:'./examples/settings.html', controller:SettingsCntl});
$route.parent(this); $route.parent(this);
// initialize the model to something useful // initialize the model to something useful
this.person = { this.person = {
name:'anonymous', name:'anonymous',
@ -67,7 +54,6 @@ The two partials are defined in the following URLs:
}; };
} }
function WelcomeCntl($route){} function WelcomeCntl($route){}
WelcomeCntl.prototype = { WelcomeCntl.prototype = {
greet: function(){ greet: function(){
@ -75,7 +61,6 @@ The two partials are defined in the following URLs:
} }
}; };
function SettingsCntl(){ function SettingsCntl(){
this.cancel(); this.cancel();
} }
@ -84,7 +69,6 @@ The two partials are defined in the following URLs:
this.form = angular.copy(this.person); this.form = angular.copy(this.person);
}, },
save: function(){ save: function(){
angular.copy(this.form, this.person); angular.copy(this.form, this.person);
window.location.hash = "#"; window.location.hash = "#";
@ -117,12 +101,8 @@ The two partials are defined in the following URLs:
# Things to notice # Things to notice
* Routes are defined in the `AppCntl` class. The initialization of the controller causes the * Routes are defined in the `AppCntl` class. The initialization of the controller causes the
initialization of the {@link api/angular.service.$route $route} service with the proper URL initialization of the {@link api/angular.service.$route $route} service with the proper URL
routes. routes.

View file

@ -3,14 +3,11 @@
@name Cookbook: Form @name Cookbook: Form
@description @description
A web application's main purpose is to present and gather data. For this reason angular strives A web application's main purpose is to present and gather data. For this reason angular strives
to make both of these operations trivial. This example shows off how you can build a simple form to to make both of these operations trivial. This example shows off how you can build a simple form to
allow a user to enter data. allow a user to enter data.
<doc:example> <doc:example>
<doc:source> <doc:source>
<script> <script>
@ -26,11 +23,9 @@ allow a user to enter data.
</script> </script>
<div ng:controller="FormController" class="example"> <div ng:controller="FormController" class="example">
<label>Name:</label><br/> <label>Name:</label><br/>
<input type="text" name="user.name" ng:required/> <br/><br/> <input type="text" name="user.name" ng:required/> <br/><br/>
<label>Address:</label><br/> <label>Address:</label><br/>
<input type="text" name="user.address.line1" size="33" ng:required/> <br/> <input type="text" name="user.address.line1" size="33" ng:required/> <br/>
<input type="text" name="user.address.city" size="12" ng:required/>, <input type="text" name="user.address.city" size="12" ng:required/>,
@ -38,7 +33,6 @@ allow a user to enter data.
<input type="text" name="user.address.zip" size="5" ng:required <input type="text" name="user.address.zip" size="5" ng:required
ng:validate="regexp:zip"/><br/><br/> ng:validate="regexp:zip"/><br/><br/>
<label>Phone:</label> <label>Phone:</label>
[ <a href="" ng:click="user.contacts.$add()">add</a> ] [ <a href="" ng:click="user.contacts.$add()">add</a> ]
<div ng:repeat="contact in user.contacts"> <div ng:repeat="contact in user.contacts">
@ -56,7 +50,6 @@ ng:validate="regexp:zip"/><br/><br/>
<pre>user={{user}}</pre> <pre>user={{user}}</pre>
</div> </div>
</doc:source> </doc:source>
<doc:scenario> <doc:scenario>
it('should show debug', function(){ it('should show debug', function(){
@ -69,13 +62,11 @@ ng:validate="regexp:zip"/><br/><br/>
expect(binding('user')).toMatch(/you@example.org/); expect(binding('user')).toMatch(/you@example.org/);
}); });
it('should remove contact', function(){ it('should remove contact', function(){
using('.example').element('a:contains(X)').click(); using('.example').element('a:contains(X)').click();
expect(binding('user')).not().toMatch(/\(234\) 555\-1212/); expect(binding('user')).not().toMatch(/\(234\) 555\-1212/);
}); });
it('should validate zip', function(){ it('should validate zip', function(){
expect(using('.example').element(':input[name=user.address.zip]').attr('className')) expect(using('.example').element(':input[name=user.address.zip]').attr('className'))
.not().toMatch(/ng-validation-error/); .not().toMatch(/ng-validation-error/);
@ -84,7 +75,6 @@ ng:validate="regexp:zip"/><br/><br/>
.toMatch(/ng-validation-error/); .toMatch(/ng-validation-error/);
}); });
it('should validate state', function(){ it('should validate state', function(){
expect(using('.example').element(':input[name=user.address.state]').attr('className')) expect(using('.example').element(':input[name=user.address.state]').attr('className'))
.not().toMatch(/ng-validation-error/); .not().toMatch(/ng-validation-error/);
@ -96,11 +86,8 @@ ng:validate="regexp:zip"/><br/><br/>
</doc:example> </doc:example>
# Things to notice # Things to notice
* The user data model is initialized {@link api/angular.directive.ng:controller controller} and is * The user data model is initialized {@link api/angular.directive.ng:controller controller} and is
available in available in
the {@link api/angular.scope scope} with the initial data. the {@link api/angular.scope scope} with the initial data.

View file

@ -3,7 +3,6 @@
@name Cookbook: Hello World @name Cookbook: Hello World
@description @description
<doc:example> <doc:example>
<doc:source> <doc:source>
Your name: <input type="text" name="name" value="World"/> Your name: <input type="text" name="name" value="World"/>
@ -19,13 +18,10 @@
</doc:scenario> </doc:scenario>
</doc:example> </doc:example>
# Things to notice # Things to notice
Take a look through the source and note: Take a look through the source and note:
* The script tag that {@link guide/dev_guide.bootstrap bootstraps} the angular environment. * The script tag that {@link guide/dev_guide.bootstrap bootstraps} the angular environment.
* The text {@link api/angular.widget.HTML input widget} which is bound to the greeting name text. * The text {@link api/angular.widget.HTML input widget} which is bound to the greeting name text.
* No need for listener registration and event firing on change events. * No need for listener registration and event firing on change events.

View file

@ -3,68 +3,48 @@
@name Cookbook @name Cookbook
@description @description
Welcome to the angular cookbook. Here we will show you typical uses of angular by example. Welcome to the angular cookbook. Here we will show you typical uses of angular by example.
# Hello World # Hello World
{@link helloworld Hello World}: The simplest possible application that demonstrates the {@link helloworld Hello World}: The simplest possible application that demonstrates the
classic Hello World! classic Hello World!
# Basic Form # Basic Form
{@link form Basic Form}: Displaying forms to the user for editing is the bread and butter {@link form Basic Form}: Displaying forms to the user for editing is the bread and butter
of web applications. Angular makes forms easy through bidirectional data binding. of web applications. Angular makes forms easy through bidirectional data binding.
# Advanced Form # Advanced Form
{@link advancedform Advanced Form}: Taking the form example to the next level and {@link advancedform Advanced Form}: Taking the form example to the next level and
providing advanced features such as dirty detection, form reverting and submit disabling if providing advanced features such as dirty detection, form reverting and submit disabling if
validation errors exist. validation errors exist.
# Model View Controller # Model View Controller
{@link mvc MVC}: Tic-Tac-Toe: Model View Controller (MVC) is a time-tested design pattern {@link mvc MVC}: Tic-Tac-Toe: Model View Controller (MVC) is a time-tested design pattern
to separate the behavior (JavaScript controller) from the presentation (HTML view). This to separate the behavior (JavaScript controller) from the presentation (HTML view). This
separation aids in maintainability and testability of your project. separation aids in maintainability and testability of your project.
# Multi-page App and Deep Linking # Multi-page App and Deep Linking
{@link deeplinking Deep Linking}: An AJAX application never navigates away from the {@link deeplinking Deep Linking}: An AJAX application never navigates away from the
first page it loads. Instead, it changes the DOM of its single page. Eliminating full-page reloads first page it loads. Instead, it changes the DOM of its single page. Eliminating full-page reloads
is what makes AJAX apps responsive, but it creates a problem in that apps with a single URL is what makes AJAX apps responsive, but it creates a problem in that apps with a single URL
prevent you from emailing links to a particular screen within your application. prevent you from emailing links to a particular screen within your application.
Deep linking tries to solve this by changing the URL anchor without reloading a page, thus Deep linking tries to solve this by changing the URL anchor without reloading a page, thus
allowing you to send links to specific screens in your app. allowing you to send links to specific screens in your app.
# Services # Services
{@link api/angular.service Services}: Services are long lived objects in your applications that are {@link api/angular.service Services}: Services are long lived objects in your applications that are
available across controllers. A collection of useful services are pre-bundled with angular but you available across controllers. A collection of useful services are pre-bundled with angular but you
will likely add your own. Services are initialized using dependency injection, which resolves the will likely add your own. Services are initialized using dependency injection, which resolves the
@ -72,11 +52,8 @@ order of initialization. This safeguards you from the perils of global state (a
implement long lived objects). implement long lived objects).
# External Resources # External Resources
{@link buzz Resources}: Web applications must be able to communicate with the external {@link buzz Resources}: Web applications must be able to communicate with the external
services to get and update data. Resources are the abstractions of external URLs which are services to get and update data. Resources are the abstractions of external URLs which are
specially tailored to angular data binding. specially tailored to angular data binding.

View file

@ -3,19 +3,15 @@
@name Cookbook: MVC @name Cookbook: MVC
@description @description
MVC allows for a clean an testable separation between the behavior (controller) and the view MVC allows for a clean an testable separation between the behavior (controller) and the view
(HTML template). A Controller is just a JavaScript class which is grafted onto the scope of the (HTML template). A Controller is just a JavaScript class which is grafted onto the scope of the
view. This makes it very easy for the controller and the view to share the model. view. This makes it very easy for the controller and the view to share the model.
The model is simply the controller's this. This makes it very easy to test the controller in The model is simply the controller's this. This makes it very easy to test the controller in
isolation since one can simply instantiate the controller and test without a view, because there is isolation since one can simply instantiate the controller and test without a view, because there is
no connection between the controller and the view. no connection between the controller and the view.
<doc:example> <doc:example>
<doc:source> <doc:source>
<script> <script>
@ -83,7 +79,6 @@ no connection between the controller and the view.
}; };
</script> </script>
<h3>Tic-Tac-Toe</h3> <h3>Tic-Tac-Toe</h3>
<div ng:controller="TicTacToeCntl"> <div ng:controller="TicTacToeCntl">
Next Player: {{nextMove}} Next Player: {{nextMove}}
@ -109,7 +104,6 @@ no connection between the controller and the view.
expect(element('.winner').text()).toEqual('Player X has won!'); expect(element('.winner').text()).toEqual('Player X has won!');
}); });
function piece(row, col) { function piece(row, col) {
element('.board tr:nth-child('+row+') td:nth-child('+col+')').click(); element('.board tr:nth-child('+row+') td:nth-child('+col+')').click();
} }
@ -117,11 +111,8 @@ no connection between the controller and the view.
</doc:example> </doc:example>
# Things to notice # Things to notice
* The controller is defined in JavaScript and has no reference to the rendering logic. * The controller is defined in JavaScript and has no reference to the rendering logic.
* The controller is instantiated by <angular/> and injected into the view. * The controller is instantiated by <angular/> and injected into the view.
* The controller can be instantiated in isolation (without a view) and the code will still execute. * The controller can be instantiated in isolation (without a view) and the code will still execute.

View file

@ -3,30 +3,23 @@
@name Developer Guide: Initializing Angular: Automatic Initiialization @name Developer Guide: Initializing Angular: Automatic Initiialization
@description @description
Angular initializes automatically when you load the angular script into your page, specifying Angular initializes automatically when you load the angular script into your page, specifying
angular's `ng:autobind` attribute with no arguments: angular's `ng:autobind` attribute with no arguments:
<script src="angular.js" ng:autobind> <script src="angular.js" ng:autobind>
From a high-level view, this is what happens during angular's automatic initialization process: From a high-level view, this is what happens during angular's automatic initialization process:
1. The browser loads the page, and then runs the angular script. 1. The browser loads the page, and then runs the angular script.
The `ng:autobind` attribute tells angular to compile and manage the whole HTML document. The The `ng:autobind` attribute tells angular to compile and manage the whole HTML document. The
compilation phase is initiated in the page's `onLoad()` handler. Angular doesn't begin processing compilation phase is initiated in the page's `onLoad()` handler. Angular doesn't begin processing
the page until after the page load is complete. the page until after the page load is complete.
2. Angular finds the root of the HTML document and creates the global variable `angular` in the 2. Angular finds the root of the HTML document and creates the global variable `angular` in the
global namespace. Everything that angular subsequently creates is bound to fields in this global global namespace. Everything that angular subsequently creates is bound to fields in this global
object. object.
3. Angular walks the DOM looking for angular widgets, directives, and markup (such as `ng:init` or 3. Angular walks the DOM looking for angular widgets, directives, and markup (such as `ng:init` or
`ng:repeat`). As angular encounters these, it creates child scopes as necessary and attaches them `ng:repeat`). As angular encounters these, it creates child scopes as necessary and attaches them
to the DOM, registers listeners on those scopes, associates any controller functions with their to the DOM, registers listeners on those scopes, associates any controller functions with their
@ -34,39 +27,29 @@ data and their part of the view, and ultimately constructs a runnable applicatio
app features two-way data-binding and a nice separation between data, presentation, and business app features two-way data-binding and a nice separation between data, presentation, and business
logic. logic.
4. For the duration of the application session (while the page is loaded), angular monitors the 4. For the duration of the application session (while the page is loaded), angular monitors the
state of the application, and updates the view and the data model whenever the state of either one state of the application, and updates the view and the data model whenever the state of either one
changes. changes.
For details on how the compiler works, see {@link dev_guide.compiler Angular HTML Compiler}. For details on how the compiler works, see {@link dev_guide.compiler Angular HTML Compiler}.
## Initialization Options ## Initialization Options
The reason why `ng:autobind` exists is because angular should not assume that the entire HTML The reason why `ng:autobind` exists is because angular should not assume that the entire HTML
document should be processed just because the `angular.js` script is included. In order to compile document should be processed just because the `angular.js` script is included. In order to compile
only a part of the document, specify the ID of the element you want to use for angular's root only a part of the document, specify the ID of the element you want to use for angular's root
element as the value of the `ng:autobind` attribute: element as the value of the `ng:autobind` attribute:
ng:autobind="angularContent" ng:autobind="angularContent"
## Auto-bootstrap with `#autobind` ## Auto-bootstrap with `#autobind`
In some rare cases you can't define the `ng:` prefix before the script tag's attribute (for In some rare cases you can't define the `ng:` prefix before the script tag's attribute (for
example, in some CMS systems). In those situations it is possible to auto-bootstrap angular by example, in some CMS systems). In those situations it is possible to auto-bootstrap angular by
appending `#autobind` to the `<script src=...>` URL, like in this snippet: appending `#autobind` to the `<script src=...>` URL, like in this snippet:
<pre> <pre>
<!doctype html> <!doctype html>
<html> <html>
@ -82,19 +65,15 @@ appending `#autobind` to the `<script src=...>` URL, like in this snippet:
</html> </html>
</pre> </pre>
As with `ng:autobind`, you can specify an element id that should be exclusively targeted for As with `ng:autobind`, you can specify an element id that should be exclusively targeted for
compilation as the value of the `#autobind`, for example: `#autobind=angularContent`. compilation as the value of the `#autobind`, for example: `#autobind=angularContent`.
## Filename Restrictions for Auto-bootstrap ## Filename Restrictions for Auto-bootstrap
In order for us to find the auto-bootstrap from a script attribute or URL fragment, the value of In order for us to find the auto-bootstrap from a script attribute or URL fragment, the value of
the `script` `src` attribute that loads the angular script must match one of these naming the `script` `src` attribute that loads the angular script must match one of these naming
conventions: conventions:
- `angular.js` - `angular.js`
- `angular-min.js` - `angular-min.js`
- `angular-x.x.x.js` - `angular-x.x.x.js`
@ -103,28 +82,20 @@ conventions:
- `angular-x.x.x-xxxxxxxx.min.js` (dev snapshot) - `angular-x.x.x-xxxxxxxx.min.js` (dev snapshot)
- `angular-bootstrap.js` (used for development of angular) - `angular-bootstrap.js` (used for development of angular)
Optionally, any of the filename formats above can be prepended with a relative or absolute URL that Optionally, any of the filename formats above can be prepended with a relative or absolute URL that
ends with `/`. ends with `/`.
## Global Angular Object ## Global Angular Object
The angular script creates a single global variable `angular` in the global namespace. All angular The angular script creates a single global variable `angular` in the global namespace. All angular
APIs are bound to fields of this global object. APIs are bound to fields of this global object.
## Related Topics ## Related Topics
* {@link dev_guide.bootstrap Initializing Angular} * {@link dev_guide.bootstrap Initializing Angular}
* {@link dev_guide.bootstrap.manual_bootstrap Manual Initialization} * {@link dev_guide.bootstrap.manual_bootstrap Manual Initialization}
## Related API ## Related API
{@link api/angular.compile Compiler API} {@link api/angular.compile Compiler API}

View file

@ -3,17 +3,14 @@
@name Developer Guide: Initializing Angular: Manual Initialization @name Developer Guide: Initializing Angular: Manual Initialization
@description @description
Letting angular handle the initialization process (bootstrapping) is a handy way to start using Letting angular handle the initialization process (bootstrapping) is a handy way to start using
angular, but advanced users who want more control over the initialization process can choose to use angular, but advanced users who want more control over the initialization process can choose to use
the manual bootstrapping method instead. the manual bootstrapping method instead.
The best way to get started with manual bootstrapping is to look at the what happens when you use The best way to get started with manual bootstrapping is to look at the what happens when you use
{@link api/angular.directive.ng:autobind ng:autobind}, by showing each step of the process {@link api/angular.directive.ng:autobind ng:autobind}, by showing each step of the process
explicitly. explicitly.
<pre> <pre>
<!doctype html> <!doctype html>
<html xmlns:ng="http://angularjs.org"> <html xmlns:ng="http://angularjs.org">
@ -32,27 +29,20 @@ Hello {{'World'}}!
</html> </html>
</pre> </pre>
This is the sequence that your code should follow if you bootstrap angular on your own: This is the sequence that your code should follow if you bootstrap angular on your own:
1. After the page is loaded, find the root of the HTML template, which is typically the root of 1. After the page is loaded, find the root of the HTML template, which is typically the root of
the document. the document.
2. Run angular's {@link dev_guide.compiler Angular HTML compiler}, which converts a template into 2. Run angular's {@link dev_guide.compiler Angular HTML compiler}, which converts a template into
an executable, bi-directionally bound application. an executable, bi-directionally bound application.
## Related Topics ## Related Topics
* {@link dev_guide.bootstrap Initializing Angular} * {@link dev_guide.bootstrap Initializing Angular}
* {@link dev_guide.bootstrap.auto_bootstrap Automatic Initialization} * {@link dev_guide.bootstrap.auto_bootstrap Automatic Initialization}
* {@link dev_guide.compiler Angular HTML compiler} * {@link dev_guide.compiler Angular HTML compiler}
## Related API ## Related API
{@link api/angular.compile Compiler API} {@link api/angular.compile Compiler API}

View file

@ -3,20 +3,16 @@
@name Developer Guide: Initializing Angular @name Developer Guide: Initializing Angular
@description @description
Initializing angular consists of loading the `angular.js` script in your page, and specifying how Initializing angular consists of loading the `angular.js` script in your page, and specifying how
angular should process and manage the page. To initialize angular you do the following: angular should process and manage the page. To initialize angular you do the following:
* Specify the angular namespace in the `<html>` page * Specify the angular namespace in the `<html>` page
* Choose which flavor of angular script to load (debug or production) * Choose which flavor of angular script to load (debug or production)
* Specify whether or not angular should process and manage the page automatically (`ng:autobind`) * Specify whether or not angular should process and manage the page automatically (`ng:autobind`)
The simplest way to initialize angular is to load the angular script and tell angular to compile The simplest way to initialize angular is to load the angular script and tell angular to compile
and manage the whole page. You do this as follows: and manage the whole page. You do this as follows:
<pre> <pre>
<!doctype html> <!doctype html>
<html xmlns:ng="http://angularjs.org"> <html xmlns:ng="http://angularjs.org">
@ -30,57 +26,40 @@ and manage the whole page. You do this as follows:
</pre> </pre>
## Specifying the Angular Namespace ## Specifying the Angular Namespace
<html xmlns:ng="http://angularjs.org"> <html xmlns:ng="http://angularjs.org">
You need to declare the angular namespace declaration in the following cases: You need to declare the angular namespace declaration in the following cases:
* For all types of browser if you are using XHTML. * For all types of browser if you are using XHTML.
* For Internet Explorer older than version 9 (because older versions of IE do not render widgets * For Internet Explorer older than version 9 (because older versions of IE do not render widgets
properly for either HTML or XHTML). properly for either HTML or XHTML).
## Creating Your Own Namespaces ## Creating Your Own Namespaces
When you are ready to define your own {@link dev_guide.compiler.widgets widgets}, you must create When you are ready to define your own {@link dev_guide.compiler.widgets widgets}, you must create
your own namespace in addition to specifying the angular namespace. You use your own namespace to your own namespace in addition to specifying the angular namespace. You use your own namespace to
form the fully qualified name for widgets that you create. form the fully qualified name for widgets that you create.
For example, you could map the alias `my` to your domain, and create a widget called `my:widget`. For example, you could map the alias `my` to your domain, and create a widget called `my:widget`.
To create your own namespace, simply add another `xmlns` tag to your page, create an alias, and set To create your own namespace, simply add another `xmlns` tag to your page, create an alias, and set
it to your unique domain: it to your unique domain:
<html xmlns:ng="http://angularjs.org" xmlns:my="http://mydomain.com"> <html xmlns:ng="http://angularjs.org" xmlns:my="http://mydomain.com">
## Loading the Angular Bootstrap Script ## Loading the Angular Bootstrap Script
The angular bootstrap script comes in two flavors; a debug script, and a production script: The angular bootstrap script comes in two flavors; a debug script, and a production script:
* angular-[version].js - This is a human-readable file, suitable for development and debugging. * angular-[version].js - This is a human-readable file, suitable for development and debugging.
* angular-[version].min.js - This is a compressed and obfuscated file, suitable for use in * angular-[version].min.js - This is a compressed and obfuscated file, suitable for use in
production. production.
## Related Topics ## Related Topics
* {@link dev_guide.bootstrap.auto_bootstrap Automatic Initialization} * {@link dev_guide.bootstrap.auto_bootstrap Automatic Initialization}
* {@link dev_guide.bootstrap.manual_bootstrap Manual Initialization} * {@link dev_guide.bootstrap.manual_bootstrap Manual Initialization}

View file

@ -3,12 +3,10 @@
@name Developer Guide: Angular HTML Compiler: Directives: Creating Custom Angular Directives @name Developer Guide: Angular HTML Compiler: Directives: Creating Custom Angular Directives
@description @description
The following code snippet shows how to define a custom directive. You define a new directive by The following code snippet shows how to define a custom directive. You define a new directive by
extending the {@link dev_guide.compiler Angular HTML compiler}. The code snippet below is a extending the {@link dev_guide.compiler Angular HTML compiler}. The code snippet below is a
simplified definition of the built-in {@link api/angular.directive.ng:bind ng:bind} directive: simplified definition of the built-in {@link api/angular.directive.ng:bind ng:bind} directive:
<pre> <pre>
angular.directive('ng:bind', function(expression, compiledElement) { angular.directive('ng:bind', function(expression, compiledElement) {
var compiler = this; var compiler = this;
@ -21,29 +19,21 @@ angular.directive('ng:bind', function(expression, compiledElement) {
}); });
</pre> </pre>
# Additional Compiler Methods for Custom Directives # Additional Compiler Methods for Custom Directives
The angular compiler exposes methods that you may need to use when writing your own widgets and The angular compiler exposes methods that you may need to use when writing your own widgets and
directives. For example, the `descend()` method lets you control whether the compiler ignores or directives. For example, the `descend()` method lets you control whether the compiler ignores or
processes child elements of the element it is compiling. For information on this and other processes child elements of the element it is compiling. For information on this and other
compiler methods, see the {@link api/angular.compile Compiler API doc}. compiler methods, see the {@link api/angular.compile Compiler API doc}.
## Related Docs ## Related Docs
* {@link dev_guide.compiler.directives Understanding Angular Directives} * {@link dev_guide.compiler.directives Understanding Angular Directives}
* {@link dev_guide.compiler.directives_widgets Comparing Directives and Widgets} * {@link dev_guide.compiler.directives_widgets Comparing Directives and Widgets}
* {@link dev_guide.compiler Angular HTML Compiler} * {@link dev_guide.compiler Angular HTML Compiler}
## Related API ## Related API
* {@link api/angular.directive Angular Directive API}. * {@link api/angular.directive Angular Directive API}.

View file

@ -3,39 +3,30 @@
@name Developer Guide: Angular HTML Compiler: Understanding Angular Directives @name Developer Guide: Angular HTML Compiler: Understanding Angular Directives
@description @description
An angular directive is a custom HTML attribute that angular knows how to process. You add them to An angular directive is a custom HTML attribute that angular knows how to process. You add them to
a template element like any other attribute. Angular directives all have a `ng:` prefix. In the a template element like any other attribute. Angular directives all have a `ng:` prefix. In the
following example, the angular directive (`ng:controller`) is a div tag: following example, the angular directive (`ng:controller`) is a div tag:
<div ng:controller> <div ng:controller>
You use angular directives to modify DOM element properties. The element you modify can be an You use angular directives to modify DOM element properties. The element you modify can be an
existing HTML element type or a custom DOM element type that you created. You can use any number of existing HTML element type or a custom DOM element type that you created. You can use any number of
directives per element. directives per element.
You add angular directives to a standard HTML tag as in the following example, in which we have You add angular directives to a standard HTML tag as in the following example, in which we have
added the {@link api/angular.directive.ng:click ng:click} directive to a button tag: added the {@link api/angular.directive.ng:click ng:click} directive to a button tag:
<button name="button1" ng:click="foo()">Click This</button> <button name="button1" ng:click="foo()">Click This</button>
In the example above, `name` is the standard HTML attribute, and `ng:click` is the angular In the example above, `name` is the standard HTML attribute, and `ng:click` is the angular
directive. The `ng:click` directive lets you implement custom behavior in an associated controller directive. The `ng:click` directive lets you implement custom behavior in an associated controller
function. function.
In the next example, we add the {@link api/angular.directive.ng:bind ng:bind} directive to a In the next example, we add the {@link api/angular.directive.ng:bind ng:bind} directive to a
`<span>` tag: `<span>` tag:
<span ng:bind="1+2"></span> <span ng:bind="1+2"></span>
The `ng:bind` directive tells angular to set up {@link dev_guide.templates.databinding data The `ng:bind` directive tells angular to set up {@link dev_guide.templates.databinding data
binding} between the data model and the view for the specified expression. When the angular {@link binding} between the data model and the view for the specified expression. When the angular {@link
dev_guide.compiler compiler} encounters an `ng:bind` directive in a template, it passes the dev_guide.compiler compiler} encounters an `ng:bind` directive in a template, it passes the
@ -44,18 +35,13 @@ the expression in the model, the view is updated to display the span text with t
expression value. expression value.
## Related Topics ## Related Topics
* {@link dev_guide.compiler Angular HTML Compiler} * {@link dev_guide.compiler Angular HTML Compiler}
* {@link dev_guide.compiler.directives.creating_directives Creating Angular Directives} * {@link dev_guide.compiler.directives.creating_directives Creating Angular Directives}
* {@link dev_guide.compiler.directives_widgets Comparing Directives and Widgets} * {@link dev_guide.compiler.directives_widgets Comparing Directives and Widgets}
## Related API: ## Related API:
* {@link api/angular.directive Directive API} * {@link api/angular.directive Directive API}
* {@link api/angular.widget Widget API} * {@link api/angular.widget Widget API}

View file

@ -3,18 +3,15 @@
@name Developer Guide: Angular HTML Compiler: Comparing Directives and Attribute Widgets @name Developer Guide: Angular HTML Compiler: Comparing Directives and Attribute Widgets
@description @description
Although directives and {@link dev_guide.compiler.widgets attribute widgets} appear the same in a Although directives and {@link dev_guide.compiler.widgets attribute widgets} appear the same in a
template (`ng:init` is a directive, `ng:repeat` is an attribute widget), there is a difference in template (`ng:init` is a directive, `ng:repeat` is an attribute widget), there is a difference in
the order in which they are evaluated. The user of existing directives or widgets cannot determine the order in which they are evaluated. The user of existing directives or widgets cannot determine
the order of evaluation. The evaluation order is the responsibility of the developer creating the order of evaluation. The evaluation order is the responsibility of the developer creating
custom directives and widgets. custom directives and widgets.
For example, consider this piece of HTML, which uses the `ng:repeat`, `ng:init`, and `ng:bind` For example, consider this piece of HTML, which uses the `ng:repeat`, `ng:init`, and `ng:bind`
widget and directives: widget and directives:
<pre> <pre>
<ul ng:init="people=['mike', 'mary']"> <ul ng:init="people=['mike', 'mary']">
<li ng:repeat="person in people" <li ng:repeat="person in people"
@ -24,36 +21,28 @@ widget and directives:
</ul> </ul>
</pre> </pre>
Notice that the order of execution matters here. Because we want to run the `ng:init="a=a+1` and Notice that the order of execution matters here. Because we want to run the `ng:init="a=a+1` and
`ng:bind="person"` once for each `person in people`, we need to execute {@link `ng:bind="person"` once for each `person in people`, we need to execute {@link
api/angular.widget.@ng:repeat ng:repeat} to make copies of the `<li>` element before we run the api/angular.widget.@ng:repeat ng:repeat} to make copies of the `<li>` element before we run the
{@link api/angular.directive.ng:init ng:init}, and {@link api/angular.directive.ng:bind ng:bind} {@link api/angular.directive.ng:init ng:init}, and {@link api/angular.directive.ng:bind ng:bind}
for each of the `<li>`copies. for each of the `<li>`copies.
If you implemented `ng:repeat` as a directive, there would be no guarantee that the attributes If you implemented `ng:repeat` as a directive, there would be no guarantee that the attributes
`ng:repeat`, `ng:init`, and `ng:bind` would be evaluated in the order they are declared, because `ng:repeat`, `ng:init`, and `ng:bind` would be evaluated in the order they are declared, because
the order of element attributes in HTML is not significant to the browser. the order of element attributes in HTML is not significant to the browser.
So, when creating a custom HTML attribute, you will have to consider whether a directive or a So, when creating a custom HTML attribute, you will have to consider whether a directive or a
widget is more appropriate. When the order of execution doesn't matter, directives are the right widget is more appropriate. When the order of execution doesn't matter, directives are the right
choice. In a situation where the order matters and one attribute should be processed with a higher choice. In a situation where the order matters and one attribute should be processed with a higher
priority than others, use a widget for the attribute that must be processed first. priority than others, use a widget for the attribute that must be processed first.
## Related Topics ## Related Topics
* {@link dev_guide.compiler.directives Understanding Angular Directives} * {@link dev_guide.compiler.directives Understanding Angular Directives}
* {@link dev_guide.compiler.widgets Understanding Angular Widgets} * {@link dev_guide.compiler.widgets Understanding Angular Widgets}
## Related API: ## Related API:
* {@link api/angular.directive Directive API} * {@link api/angular.directive Directive API}
* {@link api/angular.widget Widget API} * {@link api/angular.widget Widget API}

View file

@ -65,17 +65,13 @@ return function(linkElement){
Note: For more about widgets, see {@link dev_guide.compiler.widgets Understanding Angular Widgets} Note: For more about widgets, see {@link dev_guide.compiler.widgets Understanding Angular Widgets}
and the {@link api/angular.widget widget API reference page}. and the {@link api/angular.widget widget API reference page}.
# Compilation process for `<my:greeter>` # Compilation process for `<my:greeter>`
Here are the steps that the compiler takes in processing the page that contains the widget Here are the steps that the compiler takes in processing the page that contains the widget
definition above: definition above:
## Compile Phase ## Compile Phase
1. Recursively traverse the DOM depth-first. 1. Recursively traverse the DOM depth-first.
2. Find the angular.widget definition. 2. Find the angular.widget definition.
3. Find and execute the widget's compileElement function, which includes the following steps: 3. Find and execute the widget's compileElement function, which includes the following steps:
@ -86,10 +82,8 @@ template (i.e. any repeating elements)).
2. Extract the salutation and name HTML attributes as angular expressions. 2. Extract the salutation and name HTML attributes as angular expressions.
4. Return the aggregate link function, which includes just one link function in this example. 4. Return the aggregate link function, which includes just one link function in this example.
## Link Phase ## Link Phase
1. Execute the aggregate link function, which includes the following steps: 1. Execute the aggregate link function, which includes the following steps:
1. Create a <span> element set to the salutation class 1. Create a <span> element set to the salutation class
2. Create a <span> element set to the name class. 2. Create a <span> element set to the name class.
@ -99,17 +93,12 @@ compileElement, because that's the template.)
corresponding spans. corresponding spans.
## Related Topics ## Related Topics
* {@link dev_guide.compiler Angular HTML Compiler} * {@link dev_guide.compiler Angular HTML Compiler}
* {@link dev_guide.compiler.understanding_compiler Understanding How the Compiler Works} * {@link dev_guide.compiler.understanding_compiler Understanding How the Compiler Works}
* {@link dev_guide.compiler.testing_dom_element Testing a New DOM Element} * {@link dev_guide.compiler.testing_dom_element Testing a New DOM Element}
## Related API ## Related API
* {@link api/angular.compile angular.compile()} * {@link api/angular.compile angular.compile()}

View file

@ -3,59 +3,46 @@
@name Developer Guide: Angular HTML Compiler: Understanding Angular Markup @name Developer Guide: Angular HTML Compiler: Understanding Angular Markup
@description @description
Markup in angular is a feature that you can use in templates to transform the content of DOM Markup in angular is a feature that you can use in templates to transform the content of DOM
elements prior to the compile phase (in which elements are compiled and link functions are elements prior to the compile phase (in which elements are compiled and link functions are
returned. See the {@link dev_guide.compiler compiler docs} for details on how the compiler returned. See the {@link dev_guide.compiler compiler docs} for details on how the compiler
works.) The ability to make pre-compile changes to DOM elements lets you create shorthand for works.) The ability to make pre-compile changes to DOM elements lets you create shorthand for
{@link api/angular.widget widget} and {@link api/angular.directive directive} declarations. {@link api/angular.widget widget} and {@link api/angular.directive directive} declarations.
Angular provides one built-in markup feature: the double curly-braces used to declare binding Angular provides one built-in markup feature: the double curly-braces used to declare binding
points (between the model and view) for angular expressions. You can also create your own custom points (between the model and view) for angular expressions. You can also create your own custom
markup. markup.
# Using Double Curly-brace Markup (`{{ }}`) # Using Double Curly-brace Markup (`{{ }}`)
The double curly-brace (`{{ }}`) markup translates an enclosed expression into an {@link The double curly-brace (`{{ }}`) markup translates an enclosed expression into an {@link
api/angular.directive.ng:bind ng:bind} directive: api/angular.directive.ng:bind ng:bind} directive:
<pre> <pre>
{{expression}} {{expression}}
</pre> </pre>
is transformed to: is transformed to:
<pre> <pre>
<span ng:bind="expression"></span> <span ng:bind="expression"></span>
</pre> </pre>
Markup is useful for the simple reason that `{{1+2}}` is easier to write and understand than `<span Markup is useful for the simple reason that `{{1+2}}` is easier to write and understand than `<span
ng:bind="1+2"></span>`. After markup shorthand is expanded into the DOM elements it represents, the ng:bind="1+2"></span>`. After markup shorthand is expanded into the DOM elements it represents, the
expanded elements are then {@link dev_guide.compiler compiled} normally. expanded elements are then {@link dev_guide.compiler compiled} normally.
# Creating Custom Markup # Creating Custom Markup
Let's say you want to define markup that transforms `---` into a horizontal rule (`<hr/>`): Let's say you want to define markup that transforms `---` into a horizontal rule (`<hr/>`):
<pre> <pre>
header header
--- ---
footer footer
</pre> </pre>
should translate to: should translate to:
<pre> <pre>
header header
@ -63,10 +50,8 @@ header
footer footer
</pre> </pre>
Here is how you could extend the angular compiler to create the "---" markup: Here is how you could extend the angular compiler to create the "---" markup:
<pre> <pre>
angular.markup('---', function(text, textNode, parentElement) { angular.markup('---', function(text, textNode, parentElement) {
var compiler = this; var compiler = this;
@ -83,20 +68,16 @@ angular.markup('---', function(text, textNode, parentElement) {
}); });
</pre> </pre>
Unlike the way the compiler processes {@link api/angular.widget widgets} and {@link Unlike the way the compiler processes {@link api/angular.widget widgets} and {@link
api/angular.directive directives} (matching the name of the handler function to a DOM element or api/angular.directive directives} (matching the name of the handler function to a DOM element or
attribute name), the compiler calls every markup handler for every text node, giving the handler a attribute name), the compiler calls every markup handler for every text node, giving the handler a
chance to transform the text. The markup handler needs to find all the matches in the text. chance to transform the text. The markup handler needs to find all the matches in the text.
## Attribute Markup ## Attribute Markup
Attribute markup extends the angular compiler in a very similar way to markup, except that it Attribute markup extends the angular compiler in a very similar way to markup, except that it
allows you to modify the state of attribute text rather then the content of a node. allows you to modify the state of attribute text rather then the content of a node.
<pre> <pre>
angular.attrMarkup('extraClass', function(attrValue, attrName, element){ angular.attrMarkup('extraClass', function(attrValue, attrName, element){
if (attrName == 'additional-class') { if (attrName == 'additional-class') {
@ -106,15 +87,10 @@ angular.attrMarkup('extraClass', function(attrValue, attrName, element){
</pre> </pre>
## Related Topics ## Related Topics
* {@link dev_guide.compiler Angular HTML Compiler} * {@link dev_guide.compiler Angular HTML Compiler}
## Related API ## Related API
* {@link api/angular.compile Compiler API Reference} * {@link api/angular.compile Compiler API Reference}

View file

@ -3,24 +3,18 @@
@name Developer Guide: Angular HTML Compiler @name Developer Guide: Angular HTML Compiler
@description @description
The core of angular is its HTML compiler. The compiler processes angular directives, widgets, and The core of angular is its HTML compiler. The compiler processes angular directives, widgets, and
markup to transform a static HTML page into a dynamic web application. markup to transform a static HTML page into a dynamic web application.
The default HTML transformations that the angular compiler provides are useful for building generic The default HTML transformations that the angular compiler provides are useful for building generic
apps, but you can also extend the compiler to create a domain-specific language for building apps, but you can also extend the compiler to create a domain-specific language for building
specific types of web applications. specific types of web applications.
All compilation takes place in the web browser; no server is involved. All compilation takes place in the web browser; no server is involved.
## Related Topics ## Related Topics
* {@link dev_guide.compiler.understanding_compiler Understanding How the Compiler Works} * {@link dev_guide.compiler.understanding_compiler Understanding How the Compiler Works}
* {@link dev_guide.compiler.extending_compiler Extending the Angular Compiler} * {@link dev_guide.compiler.extending_compiler Extending the Angular Compiler}
* {@link dev_guide.compiler.testing_dom_element Testing a New DOM Element} * {@link dev_guide.compiler.testing_dom_element Testing a New DOM Element}
@ -28,8 +22,6 @@ All compilation takes place in the web browser; no server is involved.
* {@link dev_guide.compiler.directives Understanding Angular Directives} * {@link dev_guide.compiler.directives Understanding Angular Directives}
* {@link dev_guide.compiler.markup Understanding Angular Markup} * {@link dev_guide.compiler.markup Understanding Angular Markup}
## Related API ## Related API
* {@link api/angular.compile Angular Compiler API} * {@link api/angular.compile Angular Compiler API}

View file

@ -4,22 +4,15 @@
@description @description
"Testing, testing, come in, over?" "Testing, testing, come in, over?"
## Related Topics ## Related Topics
* {@link dev_guide.compiler Angular HTML Compiler} * {@link dev_guide.compiler Angular HTML Compiler}
* {@link dev_guide.compiler.understanding_compiler Understanding How the Compiler Works} * {@link dev_guide.compiler.understanding_compiler Understanding How the Compiler Works}
* {@link dev_guide.compiler.extending_compiler Extending the Angular Compiler} * {@link dev_guide.compiler.extending_compiler Extending the Angular Compiler}
## Related API ## Related API
* {@link api/angular.compile angular.compile()} * {@link api/angular.compile angular.compile()}

View file

@ -3,7 +3,6 @@
@name Developer Guide: Angular HTML Compiler: Understanding How the Compiler Works @name Developer Guide: Angular HTML Compiler: Understanding How the Compiler Works
@description @description
Every {@link api/angular.widget widget}, {@link api/angular.directive directive} and {@link Every {@link api/angular.widget widget}, {@link api/angular.directive directive} and {@link
dev_guide.compiler.markup markup} is defined with a compile function, which the angular compiler dev_guide.compiler.markup markup} is defined with a compile function, which the angular compiler
executes on each widget or directive it encounters. The compile function optionally returns a link executes on each widget or directive it encounters. The compile function optionally returns a link
@ -11,10 +10,8 @@ function. This compilation process happens automatically when the page is loade
`ng:autobind` in the script tag from which you load the angular script file. (See {@link `ng:autobind` in the script tag from which you load the angular script file. (See {@link
dev_guide.bootstrap Initializing Angular}.) dev_guide.bootstrap Initializing Angular}.)
The compile and link functions are related as follows: The compile and link functions are related as follows:
* **compile function** — Registers a listener for the widget, directive, or markup expression. The * **compile function** — Registers a listener for the widget, directive, or markup expression. The
compiler calls this function exactly once. compiler calls this function exactly once.
* **link function** — Sets up the listener registered by the compile function. This function can be * **link function** — Sets up the listener registered by the compile function. This function can be
@ -22,20 +19,16 @@ called multiple times, once per cloned DOM element. For example, in the case of
api/angular.widget.@ng:repeat repeater widget} used in a list element (`<li ng:repeat="[item in api/angular.widget.@ng:repeat repeater widget} used in a list element (`<li ng:repeat="[item in
dataset]"`), the link function gets called to set up a listener on each element in the list. dataset]"`), the link function gets called to set up a listener on each element in the list.
Note that angular's built-in widgets, directives, and markup have predefined compile and link Note that angular's built-in widgets, directives, and markup have predefined compile and link
functions that you don't need to modify. When you create your own widgets, directives, or markup, functions that you don't need to modify. When you create your own widgets, directives, or markup,
you must write compile and link functions for them. Refer to the {@link api/angular.compile you must write compile and link functions for them. Refer to the {@link api/angular.compile
Compiler API} for details. Compiler API} for details.
When the angular compiler compiles a page, it proceeds through 3 phases: Compile, Create Root When the angular compiler compiles a page, it proceeds through 3 phases: Compile, Create Root
Scope, and Link: Scope, and Link:
1. Compile Phase 1. Compile Phase
1. Recursively traverse the DOM, depth-first. 1. Recursively traverse the DOM, depth-first.
2. Look for a matching compile function of type widget, then markup, then directive. 2. Look for a matching compile function of type widget, then markup, then directive.
3. If a compile function is found then execute it. 3. If a compile function is found then execute it.
@ -43,46 +36,34 @@ Scope, and Link:
function with all link functions returned previously by step 3. function with all link functions returned previously by step 3.
5. Repeat steps 3 and 4 for all compile functions found. 5. Repeat steps 3 and 4 for all compile functions found.
The result of the compilation phase is an aggregate link function, which comprises all of the The result of the compilation phase is an aggregate link function, which comprises all of the
individual link functions. individual link functions.
2. Create Root Scope Phase 2. Create Root Scope Phase
* Inject all services into the root scope. * Inject all services into the root scope.
3. Link Phase 3. Link Phase
1. Execute the aggregate link function with the root scope. The aggregate link function calls 1. Execute the aggregate link function with the root scope. The aggregate link function calls
all of the individual link functions that were generated in the compile phase. all of the individual link functions that were generated in the compile phase.
2. If there are any clones of the DOM caused by repeating elements, call the link function 2. If there are any clones of the DOM caused by repeating elements, call the link function
multiple times, one for each repeating item. multiple times, one for each repeating item.
Note that while the compile function is executed exactly once, the link function can be executed Note that while the compile function is executed exactly once, the link function can be executed
multiple times, for example, once for each iteration in a repeater. multiple times, for example, once for each iteration in a repeater.
The angular compiler exposes methods that you will need to make use of when writing your own The angular compiler exposes methods that you will need to make use of when writing your own
widgets and directives. For information on these methods, see the {@link api/angular.compile widgets and directives. For information on these methods, see the {@link api/angular.compile
Compiler API doc}. Compiler API doc}.
## Related Topics ## Related Topics
* {@link dev_guide.compiler Angular HTML Compiler} * {@link dev_guide.compiler Angular HTML Compiler}
* {@link dev_guide.compiler.extending_compiler Extending the Angular Compiler} * {@link dev_guide.compiler.extending_compiler Extending the Angular Compiler}
* {@link dev_guide.compiler.testing_dom_element Testing a New DOM Element} * {@link dev_guide.compiler.testing_dom_element Testing a New DOM Element}
## Related API ## Related API
* {@link api/angular.compile angular.compile()} * {@link api/angular.compile angular.compile()}

View file

@ -3,24 +3,19 @@
@name Developer Guide: Angular HTML Compiler: Widgets: Creating Custom Widgets @name Developer Guide: Angular HTML Compiler: Widgets: Creating Custom Widgets
@description @description
When you create your own widgets, you must set up your own namespace for them. (See When you create your own widgets, you must set up your own namespace for them. (See
dev_guide.bootstrap Initializing Angular} for information about namespaces in angular.) dev_guide.bootstrap Initializing Angular} for information about namespaces in angular.)
Let's say we would like to create a new element type in the namespace `my` that can watch an Let's say we would like to create a new element type in the namespace `my` that can watch an
expression and `alert()` the user with each new value: expression and `alert()` the user with each new value:
<pre> <pre>
// An element widget // An element widget
<my:watch exp="name"/> <my:watch exp="name"/>
</pre> </pre>
You can implement `my:watch` like this: You can implement `my:watch` like this:
<pre> <pre>
angular.widget('my:watch', function(compileElement) { angular.widget('my:watch', function(compileElement) {
var compiler = this; var compiler = this;
@ -35,15 +30,11 @@ angular.widget('my:watch', function(compileElement) {
</pre> </pre>
# Creating a Custom Attribute Widget # Creating a Custom Attribute Widget
Let's implement the same widget as in the example in Defining an Element Widget, but this time as Let's implement the same widget as in the example in Defining an Element Widget, but this time as
an attribute that can be added to any existing DOM element: an attribute that can be added to any existing DOM element:
<pre> <pre>
// An attribute widget (my-watch) in a div tag // An attribute widget (my-watch) in a div tag
<div my-watch="name">text</div> <div my-watch="name">text</div>
@ -62,11 +53,8 @@ angular.widget('@my:watch', function(expression, compileElement) {
</pre> </pre>
# Live Example of a Custom Element Widget # Live Example of a Custom Element Widget
<doc:example> <doc:example>
<doc:source> <doc:source>
<script> <script>
@ -88,29 +76,21 @@ angular.widget('my:time', function(compileElement){
</doc:example> </doc:example>
# Additional Compiler Methods for Custom Widgets # Additional Compiler Methods for Custom Widgets
The angular compiler exposes methods that you may need to use of when writing your own widgets and The angular compiler exposes methods that you may need to use of when writing your own widgets and
directives. For example, the `descend()` method lets you control whether the compiler ignores or directives. For example, the `descend()` method lets you control whether the compiler ignores or
processes child elements of the element it is compiling. For information on this and other processes child elements of the element it is compiling. For information on this and other
compiler methods, see the {@link api/angular.compile Compiler API doc}. compiler methods, see the {@link api/angular.compile Compiler API doc}.
## Related Topics ## Related Topics
* {@link dev_guide.compiler Angular HTML Compiler} * {@link dev_guide.compiler Angular HTML Compiler}
* {@link dev_guide.compiler.directives Angular Directives} * {@link dev_guide.compiler.directives Angular Directives}
* {@link dev_guide.compiler.widgets Angular Widgets} * {@link dev_guide.compiler.widgets Angular Widgets}
* {@link dev_guide.compiler.directives.creating_directives Creating Custom Directives} * {@link dev_guide.compiler.directives.creating_directives Creating Custom Directives}
## Related API ## Related API
* {@link api/angular.compile Compiler API} * {@link api/angular.compile Compiler API}

View file

@ -3,24 +3,19 @@
@name Developer Guide: Angular HTML Compiler: Understanding Angular Widgets @name Developer Guide: Angular HTML Compiler: Understanding Angular Widgets
@description @description
Widgets are DOM elements that the browser doesn't already understand. Angular provides some Widgets are DOM elements that the browser doesn't already understand. Angular provides some
built-in widgets (such as {@link api/angular.widget.@ng:repeat ng:repeat}), and you can create your built-in widgets (such as {@link api/angular.widget.@ng:repeat ng:repeat}), and you can create your
own custom widgets. own custom widgets.
Widgets are intended to manipulate the DOM tree by adding new elements (unlike {@link Widgets are intended to manipulate the DOM tree by adding new elements (unlike {@link
dev_guide.compiler.directives angular directives}, which are intended to modify only element dev_guide.compiler.directives angular directives}, which are intended to modify only element
properties). properties).
Widgets come in two types: Widgets come in two types:
* Element Widget — A custom DOM element. An example of a custom element is shown in {@link * Element Widget — A custom DOM element. An example of a custom element is shown in {@link
dev_guide.compiler.widgets.creating_widgets Creating Custom Widgets}. dev_guide.compiler.widgets.creating_widgets Creating Custom Widgets}.
* Attribute Widget — A custom attribute on an existing DOM element. An attribute widget is similar * Attribute Widget — A custom attribute on an existing DOM element. An attribute widget is similar
to an angular directive, with the main difference being that an attribute widget will always be to an angular directive, with the main difference being that an attribute widget will always be
processed before any directives that are specified on the same element. Only one attribute widget processed before any directives that are specified on the same element. Only one attribute widget
@ -29,19 +24,13 @@ dev_guide.compiler.widgets.creating_widgets Creating Custom Widgets}.
## Related Topics ## Related Topics
* {@link dev_guide.compiler Angular HTML Compiler} * {@link dev_guide.compiler Angular HTML Compiler}
* {@link dev_guide.compiler.directives Angular Directives} * {@link dev_guide.compiler.directives Angular Directives}
* {@link dev_guide.compiler.widgets.creating_widgets Creating Custom Widgets} * {@link dev_guide.compiler.widgets.creating_widgets Creating Custom Widgets}
* {@link dev_guide.compiler.directives.creating_directives Creating Custom Directives} * {@link dev_guide.compiler.directives.creating_directives Creating Custom Directives}
## Related API ## Related API
* {@link api/angular.compile Compiler API} * {@link api/angular.compile Compiler API}

View file

@ -3,41 +3,31 @@
@name Developer Guide: About Dependency Injection (DI) @name Developer Guide: About Dependency Injection (DI)
@description @description
Dependency Injection (DI) is an object-oriented software design pattern that supports the Dependency Injection (DI) is an object-oriented software design pattern that supports the
decoupling and dependency management of application components. decoupling and dependency management of application components.
The idea behind DI is to decouple each component from all of the other components that it depends The idea behind DI is to decouple each component from all of the other components that it depends
on to do its particular job. The way this is done in DI is by moving the responsibility for on to do its particular job. The way this is done in DI is by moving the responsibility for
managing dependencies out of each individual component and into a provider component. The provider managing dependencies out of each individual component and into a provider component. The provider
(or injector) component manages the life cycles and dependencies for all of the other components in (or injector) component manages the life cycles and dependencies for all of the other components in
an application. an application.
Angular has a built-in dependency management subsystem that helps to make your applications easier Angular has a built-in dependency management subsystem that helps to make your applications easier
to develop, understand, and test. to develop, understand, and test.
For more information on DI in general, see {@link http://en.wikipedia.org/wiki/Dependency_injection For more information on DI in general, see {@link http://en.wikipedia.org/wiki/Dependency_injection
Dependency Injection} at Wikipedia, and {@link http://martinfowler.com/articles/injection.html Dependency Injection} at Wikipedia, and {@link http://martinfowler.com/articles/injection.html
Inversion of Control} by Martin Fowler, or read about DI in your favorite software design pattern Inversion of Control} by Martin Fowler, or read about DI in your favorite software design pattern
book. book.
## Related Topics ## Related Topics
* {@link dev_guide.di.understanding_di Understanding DI in Angular} * {@link dev_guide.di.understanding_di Understanding DI in Angular}
* {@link dev_guide.services Angular Services} * {@link dev_guide.services Angular Services}
## Related API ## Related API
* {@link api/angular.service Service API} * {@link api/angular.service Service API}
* {@link api/angular.injector Angular Injector API} * {@link api/angular.injector Angular Injector API}

View file

@ -4,21 +4,16 @@
@description @description
While DI is widely used in statically typed languages such as Java or C++, it has not been widely While DI is widely used in statically typed languages such as Java or C++, it has not been widely
used in JavaScript. Angular brings the benefits of DI into JavaScript apps. used in JavaScript. Angular brings the benefits of DI into JavaScript apps.
In angular, DI is implemented as a subsystem that manages dependencies between services, In angular, DI is implemented as a subsystem that manages dependencies between services,
controllers, widgets, and filters. The most important of these are {@link api/angular.service controllers, widgets, and filters. The most important of these are {@link api/angular.service
services}. services}.
Services are objects that handle common tasks in web applications. Angular provides several{@link Services are objects that handle common tasks in web applications. Angular provides several{@link
api/angular.service built-in services}, and you can create your own custom services. api/angular.service built-in services}, and you can create your own custom services.
The main job of angular's DI subsystem is to provide services to angular components that depend on The main job of angular's DI subsystem is to provide services to angular components that depend on
them. The way the DI subsystem provides services is as follows: all services are registered with them. The way the DI subsystem provides services is as follows: all services are registered with
angular's {@link api/angular.service service API}, and all components that depend on services angular's {@link api/angular.service service API}, and all components that depend on services
@ -27,13 +22,10 @@ manages the creation of service objects and the provision of those objects to th
need them, at the time they need them. The following illustration steps through the sequence of need them, at the time they need them. The following illustration steps through the sequence of
events: events:
<img src="img/guide/di_sequence_final.png"> <img src="img/guide/di_sequence_final.png">
In the illustration above, the dependency injection sequence proceeds as follows: In the illustration above, the dependency injection sequence proceeds as follows:
1. Service factory functions are registered with angular's service factory repository. 1. Service factory functions are registered with angular's service factory repository.
2. `ng:autobind` triggers angular's bootstrap sequence, during which angular compiles the template, 2. `ng:autobind` triggers angular's bootstrap sequence, during which angular compiles the template,
creates the root scope, and creates the dependency injector. creates the root scope, and creates the dependency injector.
@ -45,22 +37,17 @@ factory function from the service factory repository to construct it.
6. DI provides the instance of $xhr service to the PhoneListCtrl controller constructor 6. DI provides the instance of $xhr service to the PhoneListCtrl controller constructor
## How Scope Relates to DI ## How Scope Relates to DI
The {@link api/angular.injector injector} is responsible for resolving the service dependencies in The {@link api/angular.injector injector} is responsible for resolving the service dependencies in
the application. It gets created and configured with the creation of a root scope. The injector the application. It gets created and configured with the creation of a root scope. The injector
caches instances of services, with the services cache bound to the root scope. caches instances of services, with the services cache bound to the root scope.
Different root scopes have different instances of the injector. While typical angular applications Different root scopes have different instances of the injector. While typical angular applications
will only have one root scope (and hence the services will act like application singletons), in will only have one root scope (and hence the services will act like application singletons), in
tests it is important to not share singletons across test invocations for isolation reasons. We tests it is important to not share singletons across test invocations for isolation reasons. We
achieve the necessary isolation by having each test create its own separate root scope. achieve the necessary isolation by having each test create its own separate root scope.
<pre> <pre>
// create a root scope // create a root scope
var rootScope = angular.scope(); var rootScope = angular.scope();
@ -68,50 +55,40 @@ var rootScope = angular.scope();
var myService = rootScope.$service('myService'); var myService = rootScope.$service('myService');
</pre> </pre>
## Inferring dependencies from the signature of the factory function or constructor ## Inferring dependencies from the signature of the factory function or constructor
**EXPERIMENTAL FEATURE**: This is an experimental feature. See the important note at the end of **EXPERIMENTAL FEATURE**: This is an experimental feature. See the important note at the end of
this section for drawbacks. this section for drawbacks.
We resort to `$inject` and our own annotation because there is no way in JavaScript to get a list We resort to `$inject` and our own annotation because there is no way in JavaScript to get a list
of arguments. Or is there? It turns out that calling `.toString()` on a function returns the of arguments. Or is there? It turns out that calling `.toString()` on a function returns the
function declaration along with the argument names as shown below: function declaration along with the argument names as shown below:
<pre> <pre>
function myFn(a,b){} function myFn(a,b){}
expect(myFn.toString()).toEqual('function myFn(a,b){}'); expect(myFn.toString()).toEqual('function myFn(a,b){}');
</pre> </pre>
This means that angular can infer the function names after all and use that information to generate This means that angular can infer the function names after all and use that information to generate
the `$inject` annotation automatically. Therefore the following two function definitions are the `$inject` annotation automatically. Therefore the following two function definitions are
equivalent: equivalent:
<pre> <pre>
// given a user defined service // given a user defined service
angular.service('serviceA', ...); angular.service('serviceA', ...);
// inject '$window', 'serviceA', curry 'name'; // inject '$window', 'serviceA', curry 'name';
function fnA($window, serviceA, name){}; function fnA($window, serviceA, name){};
fnA.$inject = ['$window', 'serviceA']; fnA.$inject = ['$window', 'serviceA'];
// inject '$window', 'serviceA', curry 'name'; // inject '$window', 'serviceA', curry 'name';
function fnB($window, serviceA_, name){}; function fnB($window, serviceA_, name){};
// implies: fnB.$inject = ['$window', 'serviceA']; // implies: fnB.$inject = ['$window', 'serviceA'];
</pre> </pre>
If angular does not find a `$inject` annotation on the function, then it calls the `.toString()` If angular does not find a `$inject` annotation on the function, then it calls the `.toString()`
method and tries to infer what should be injected using the following rules: method and tries to infer what should be injected using the following rules:
* Any argument starting with `$` is an angular service and will be added to the `$inject` property * Any argument starting with `$` is an angular service and will be added to the `$inject` property
array array
* Any argument ending with `_` will be added to the `$inject` property array (angular strips the * Any argument ending with `_` will be added to the `$inject` property array (angular strips the
@ -119,7 +96,6 @@ array
* All arguments following an argument which has neither `$` nor `_` , must not have `$` nor `_` * All arguments following an argument which has neither `$` nor `_` , must not have `$` nor `_`
(these are free arguments for {@link http://en.wikipedia.org/wiki/Currying currying}) (these are free arguments for {@link http://en.wikipedia.org/wiki/Currying currying})
**IMPORTANT** **IMPORTANT**
Minifiers/obfuscators change the names of function arguments and will therefore break the `$inject` Minifiers/obfuscators change the names of function arguments and will therefore break the `$inject`
inference. For this reason, either explicitly declare the `$inject` or do not use inference. For this reason, either explicitly declare the `$inject` or do not use
@ -127,15 +103,10 @@ minifiers/obfuscators. In the future, we may provide a pre-processor which will
code and insert the `$inject` into the source code so that it can be minified/obfuscated. code and insert the `$inject` into the source code so that it can be minified/obfuscated.
## Related Topics ## Related Topics
* {@link dev_guide.services Angular Services} * {@link dev_guide.services Angular Services}
## Related API ## Related API
* {@link api/angular.service Services API} * {@link api/angular.service Services API}

View file

@ -3,11 +3,9 @@
@name Developer Guide: DI: Using DI in Controllers @name Developer Guide: DI: Using DI in Controllers
@description @description
The most common place to use dependency injection in angular applications is in {@link The most common place to use dependency injection in angular applications is in {@link
dev_guide.mvc.understanding_controller controllers}. Here is a simple example: dev_guide.mvc.understanding_controller controllers}. Here is a simple example:
<pre> <pre>
function MyController($route){ function MyController($route){
// configure the route service // configure the route service
@ -16,14 +14,12 @@ function MyController($route){
MyController.$inject = ['$route']; MyController.$inject = ['$route'];
</pre> </pre>
In this example, the `MyController` constructor function takes one argument, the {@link In this example, the `MyController` constructor function takes one argument, the {@link
api/angular.service.$route $route} service. Angular is then responsible for supplying the instance api/angular.service.$route $route} service. Angular is then responsible for supplying the instance
of `$route` to the controller when the constructor is instantiated. There are two ways to cause of `$route` to the controller when the constructor is instantiated. There are two ways to cause
controller instantiation by configuring routes with the `$route` service, or by referencing the controller instantiation by configuring routes with the `$route` service, or by referencing the
controller from the HTML template, as follows: controller from the HTML template, as follows:
<pre> <pre>
<!doctype html> <!doctype html>
<html xmlns:ng="http://angularjs.org" ng:controller="MyController"> <html xmlns:ng="http://angularjs.org" ng:controller="MyController">
@ -34,33 +30,25 @@ controller from the HTML template, as follows:
</html> </html>
</pre> </pre>
When angular is instantiating your controller, it needs to know what services, if any, should be When angular is instantiating your controller, it needs to know what services, if any, should be
injected (passed in as arguments) into the controller. Since there is no reflection in JavaScript, injected (passed in as arguments) into the controller. Since there is no reflection in JavaScript,
we have to supply this information to angular in the form of an additional property on the we have to supply this information to angular in the form of an additional property on the
controller constructor function called `$inject`. Think of it as annotations for JavaScript. controller constructor function called `$inject`. Think of it as annotations for JavaScript.
<pre> <pre>
MyController.$inject = ['$route']; MyController.$inject = ['$route'];
</pre> </pre>
The information in `$inject` is then used by the {@link api/angular.injector injector} to call the The information in `$inject` is then used by the {@link api/angular.injector injector} to call the
function with the correct arguments. function with the correct arguments.
## Related Topics ## Related Topics
* {@link dev_guide.di About Dependency Injection} * {@link dev_guide.di About Dependency Injection}
* {@link dev_guide.di.understanding_di Understanding Dependency Injection in Angular} * {@link dev_guide.di.understanding_di Understanding Dependency Injection in Angular}
* {@link dev_guide.services Angular Services} * {@link dev_guide.services Angular Services}
## Related API ## Related API
* {@link api/angular.injector Angular Injector API} * {@link api/angular.injector Angular Injector API}

View file

@ -3,31 +3,24 @@
@name Developer Guide: Understanding Angular Expressions @name Developer Guide: Understanding Angular Expressions
@description @description
Expressions are {@link dev_guide.templates.databinding bindings} that you write in HTML and embed Expressions are {@link dev_guide.templates.databinding bindings} that you write in HTML and embed
in templates in order to create views in angular. Angular expressions are similar but not in templates in order to create views in angular. Angular expressions are similar but not
equivalent to JavaScript expressions. equivalent to JavaScript expressions.
For example, these are all valid expressions in angular: For example, these are all valid expressions in angular:
* `1+2={{1+2}}` * `1+2={{1+2}}`
* `3*10|currency` * `3*10|currency`
* `Hello {{name}}!` * `Hello {{name}}!`
* `Hello {{'World'}}!` * `Hello {{'World'}}!`
## Angular Expressions vs. JS Expressions ## Angular Expressions vs. JS Expressions
It might be tempting to think of angular view expressions as JavaScript expressions, but that is It might be tempting to think of angular view expressions as JavaScript expressions, but that is
not entirely correct. Angular does not use a simple JavaScript eval of the expression text. You can not entirely correct. Angular does not use a simple JavaScript eval of the expression text. You can
think of angular expressions as JavaScript expressions with these differences: think of angular expressions as JavaScript expressions with these differences:
* **Attribute Evaluation:** evaluation of all attributes are against the current scope, not to the * **Attribute Evaluation:** evaluation of all attributes are against the current scope, not to the
global window as in JavaScript. global window as in JavaScript.
* **Forgiving:** expression evaluation is forgiving to undefined and null, unlike in JavaScript. * **Forgiving:** expression evaluation is forgiving to undefined and null, unlike in JavaScript.
@ -38,12 +31,10 @@ conditionals, loops, or throw.
human-readable format. human-readable format.
* **The $:** angular reserves this prefix to differentiate its API names from others. * **The $:** angular reserves this prefix to differentiate its API names from others.
If, on the other hand, you do want to run arbitrary JavaScript code, you should make it a If, on the other hand, you do want to run arbitrary JavaScript code, you should make it a
controller method and call that. If you want to `eval()` an angular expression from JavaScript, use controller method and call that. If you want to `eval()` an angular expression from JavaScript, use
the `Scope:$eval()` method. the `Scope:$eval()` method.
## Example ## Example
<doc:example> <doc:example>
<doc:source> <doc:source>
@ -56,10 +47,8 @@ the `Scope:$eval()` method.
</doc:scenario> </doc:scenario>
</doc:example> </doc:example>
You can try evaluating different expressions here: You can try evaluating different expressions here:
<doc:example> <doc:example>
<doc:source> <doc:source>
<div ng:init="exprs=[]" class="expressions"> <div ng:init="exprs=[]" class="expressions">
@ -85,18 +74,14 @@ You can try evaluating different expressions here:
</doc:example> </doc:example>
# Attribute Evaluation # Attribute Evaluation
Evaluation of all attributes takes place against the current scope. Unlike JavaScript, where names Evaluation of all attributes takes place against the current scope. Unlike JavaScript, where names
default to global window properties, angular expressions have to use `$window` to refer to the default to global window properties, angular expressions have to use `$window` to refer to the
global object. For example, if you want to call `alert()`, which is defined on `window`, an global object. For example, if you want to call `alert()`, which is defined on `window`, an
expression must use `$window.alert()`. This is done intentionally to prevent accidental access to expression must use `$window.alert()`. This is done intentionally to prevent accidental access to
the global state (a common source of subtle bugs). the global state (a common source of subtle bugs).
<doc:example> <doc:example>
<doc:source> <doc:source>
<div class="example2" ng:init="$window = $service('$window')"> <div class="example2" ng:init="$window = $service('$window')">
@ -121,67 +106,50 @@ the global state (a common source of subtle bugs).
</doc:scenario> </doc:scenario>
</doc:example> </doc:example>
## Forgiving ## Forgiving
Expression evaluation is forgiving to undefined and null. In JavaScript, evaluating `a.b.c` throws Expression evaluation is forgiving to undefined and null. In JavaScript, evaluating `a.b.c` throws
an exception if `a` is not an object. While this makes sense for a general purpose language, the an exception if `a` is not an object. While this makes sense for a general purpose language, the
expression evaluations are primarily used for data binding, which often look like this: expression evaluations are primarily used for data binding, which often look like this:
{{a.b.c}} {{a.b.c}}
It makes more sense to show nothing than to throw an exception if `a` is undefined (perhaps we are It makes more sense to show nothing than to throw an exception if `a` is undefined (perhaps we are
waiting for the server response, and it will become defined soon). If expression evaluation wasn't waiting for the server response, and it will become defined soon). If expression evaluation wasn't
forgiving we'd have to write bindings that clutter the code, for example: `{{((a||{}).b||{}).c}}` forgiving we'd have to write bindings that clutter the code, for example: `{{((a||{}).b||{}).c}}`
Similarly, invoking a function `a.b.c()` on undefined or null simply returns undefined. Similarly, invoking a function `a.b.c()` on undefined or null simply returns undefined.
Assignments work the same way in reverse: Assignments work the same way in reverse:
a.b.c = 10 a.b.c = 10
...creates the intermediary objects even if a is undefined. ...creates the intermediary objects even if a is undefined.
## No Control Flow Statements ## No Control Flow Statements
You cannot write a control flow statement in an expression. The reason behind this is core to the You cannot write a control flow statement in an expression. The reason behind this is core to the
angular philosophy that application logic should be in controllers, not in the view. If you need a angular philosophy that application logic should be in controllers, not in the view. If you need a
conditional (including ternary operators), loop, or to throw from a view expression, delegate to a conditional (including ternary operators), loop, or to throw from a view expression, delegate to a
JavaScript method instead. JavaScript method instead.
## Type Augmentation ## Type Augmentation
Built-in types have methods like `[].push()`, but the richness of these methods is limited. Built-in types have methods like `[].push()`, but the richness of these methods is limited.
Consider the example below, which allows you to do a simple search over a canned set of contacts. Consider the example below, which allows you to do a simple search over a canned set of contacts.
The example would be much more complicated if we did not have the `Array:$filter()`. There is no The example would be much more complicated if we did not have the `Array:$filter()`. There is no
built-in method on `Array` called {@link api/angular.Array.filter $filter} and angular doesn't add built-in method on `Array` called {@link api/angular.Array.filter $filter} and angular doesn't add
it to `Array.prototype` because that could collide with other JavaScript frameworks. it to `Array.prototype` because that could collide with other JavaScript frameworks.
For this reason the scope expression evaluator augments the built-in types to make them act like For this reason the scope expression evaluator augments the built-in types to make them act like
they have extra methods. The actual method for `$filter()` is `angular.Array.filter()`. You can they have extra methods. The actual method for `$filter()` is `angular.Array.filter()`. You can
call it from JavaScript. call it from JavaScript.
Extensions: You can further extend the expression vocabulary by adding new methods to Extensions: You can further extend the expression vocabulary by adding new methods to
`angular.Array` or `angular.String`, etc. `angular.Array` or `angular.String`, etc.
<doc:example> <doc:example>
<doc:source> <doc:source>
<div ng:init="friends = [ <div ng:init="friends = [
@ -206,48 +174,36 @@ Extensions: You can further extend the expression vocabulary by adding new metho
input('searchText').enter('a'); input('searchText').enter('a');
expect(tr.count()).toBe(2); expect(tr.count()).toBe(2);
}); });
</doc:scenario> </doc:scenario>
</doc:example> </doc:example>
## Filters ## Filters
When presenting data to the user, you might need to convert the data from its raw format to a When presenting data to the user, you might need to convert the data from its raw format to a
user-friendly format. For example, you might have a data object that needs to be formatted user-friendly format. For example, you might have a data object that needs to be formatted
according to the locale before displaying it to the user. You can pass expressions through a chain according to the locale before displaying it to the user. You can pass expressions through a chain
of filters like this: of filters like this:
name | uppercase name | uppercase
The expression evaluator simply passes the value of name to angular.filter.uppercase. The expression evaluator simply passes the value of name to angular.filter.uppercase.
Chain filters using this syntax: Chain filters using this syntax:
value | filter1 | filter2 value | filter1 | filter2
You can also pass colon-delimited arguments to filters, for example, to display the number 123 with You can also pass colon-delimited arguments to filters, for example, to display the number 123 with
2 decimal points: 2 decimal points:
123 | number:2 123 | number:2
# The $ # The $
You might be wondering, what is the significance of the $ prefix? It is simply a prefix that You might be wondering, what is the significance of the $ prefix? It is simply a prefix that
angular uses, to differentiate its API names from others. If angular didn't use $, then evaluating angular uses, to differentiate its API names from others. If angular didn't use $, then evaluating
`a.length()` would return undefined because neither a nor angular define such a property. `a.length()` would return undefined because neither a nor angular define such a property.
Consider that in a future version of angular we might choose to add a length method, in which case Consider that in a future version of angular we might choose to add a length method, in which case
the behavior of the expression would change. Worse yet, you the developer could create a length the behavior of the expression would change. Worse yet, you the developer could create a length
property and then we would have a collision. This problem exists because angular augments existing property and then we would have a collision. This problem exists because angular augments existing
@ -255,16 +211,11 @@ objects with additional behavior. By prefixing its additions with $ we are reser
so that angular developers and developers who use angular can develop in harmony without collisions. so that angular developers and developers who use angular can develop in harmony without collisions.
## Related Topics ## Related Topics
* {@link dev_guide.compiler.markup Understanding Angular Markup} * {@link dev_guide.compiler.markup Understanding Angular Markup}
* {@link dev_guide.templates.filters Understanding Angular Filters} * {@link dev_guide.templates.filters Understanding Angular Filters}
## Related API ## Related API
* {@link api/angular.compile Angular Compiler API} * {@link api/angular.compile Angular Compiler API}

View file

@ -3,30 +3,24 @@
@name Developer Guide: Introduction @name Developer Guide: Introduction
@description @description
Angular is pure client-side technology, written entirely in JavaScript. It works with the Angular is pure client-side technology, written entirely in JavaScript. It works with the
long-established technologies of the web (HTML, CSS, and JavaScript) to make the development of web long-established technologies of the web (HTML, CSS, and JavaScript) to make the development of web
apps easier and faster than ever before. apps easier and faster than ever before.
One important way that angular simplifies web development is by increasing the level of abstraction One important way that angular simplifies web development is by increasing the level of abstraction
between the developer and most low-level web app development tasks. Angular automatically takes between the developer and most low-level web app development tasks. Angular automatically takes
care of many of these tasks, including: care of many of these tasks, including:
* DOM Manipulation * DOM Manipulation
* Setting Up Listeners and Notifiers * Setting Up Listeners and Notifiers
* Input Validation * Input Validation
Because angular handles much of the work involved in these tasks, developers can concentrate more Because angular handles much of the work involved in these tasks, developers can concentrate more
on application logic and less on repetitive, error-prone, lower-level coding. on application logic and less on repetitive, error-prone, lower-level coding.
At the same time that angular simplifies the development of web apps, it brings relatively At the same time that angular simplifies the development of web apps, it brings relatively
sophisticated techniques to the client-side, including: sophisticated techniques to the client-side, including:
* Separation of data, application logic, and presentation components * Separation of data, application logic, and presentation components
* Data Binding between data and presentation components * Data Binding between data and presentation components
* Services (common web app operations, implemented as substitutable objects) * Services (common web app operations, implemented as substitutable objects)
@ -34,18 +28,14 @@ sophisticated techniques to the client-side, including:
* An extensible HTML compiler (written entirely in JavaScript) * An extensible HTML compiler (written entirely in JavaScript)
* Ease of Testing * Ease of Testing
These techniques have been for the most part absent from the client-side for far too long. These techniques have been for the most part absent from the client-side for far too long.
## Single-page / Round-trip Applications ## Single-page / Round-trip Applications
You can use angular to develop both single-page and round-trip apps, but angular is designed You can use angular to develop both single-page and round-trip apps, but angular is designed
primarily for developing single-page apps. Angular supports browser history, forward and back primarily for developing single-page apps. Angular supports browser history, forward and back
buttons, and bookmarking in single-page apps. buttons, and bookmarking in single-page apps.
You normally wouldn't want to load angular with every page change, as would be the case with using You normally wouldn't want to load angular with every page change, as would be the case with using
angular in a round-trip app. However, it would make sense to do so if you were adding a subset of angular in a round-trip app. However, it would make sense to do so if you were adding a subset of
angular's features (for example, templates to leverage angular's data-binding feature) to an angular's features (for example, templates to leverage angular's data-binding feature) to an

View file

@ -3,31 +3,23 @@
@name Developer Guide: About MVC in Angular @name Developer Guide: About MVC in Angular
@description @description
While Model-View-Controller (MVC) has acquired different shades of meaning over the years since it While Model-View-Controller (MVC) has acquired different shades of meaning over the years since it
first appeared, angular incorporates the basic principles behind the original {@link first appeared, angular incorporates the basic principles behind the original {@link
http://en.wikipedia.org/wiki/Modelviewcontroller MVC} software design pattern into its way of http://en.wikipedia.org/wiki/Modelviewcontroller MVC} software design pattern into its way of
building client-side web applications. building client-side web applications.
The MVC pattern greatly summarized: The MVC pattern greatly summarized:
* Separate applications into distinct presentation, data, and logic components * Separate applications into distinct presentation, data, and logic components
* Encourage loose coupling between these components * Encourage loose coupling between these components
Along with {@link dev_guide.services services} and {@link dev_guide.di dependency injection}, MVC Along with {@link dev_guide.services services} and {@link dev_guide.di dependency injection}, MVC
makes angular applications better structured, easier to maintain and more testable. makes angular applications better structured, easier to maintain and more testable.
The following topics explain how angular incorporates the MVC pattern into the angular way of The following topics explain how angular incorporates the MVC pattern into the angular way of
developing web applications: developing web applications:
* {@link dev_guide.mvc.understanding_model Understanding the Model Component} * {@link dev_guide.mvc.understanding_model Understanding the Model Component}
* {@link dev_guide.mvc.understanding_controller Understanding the Controller Component} * {@link dev_guide.mvc.understanding_controller Understanding the Controller Component}
* {@link dev_guide.mvc.understanding_view Understanding the View Component} * {@link dev_guide.mvc.understanding_view Understanding the View Component}

View file

@ -3,56 +3,43 @@
@name Developer Guide: About MVC in Angular: Understanding the Controller Component @name Developer Guide: About MVC in Angular: Understanding the Controller Component
@description @description
In angular, a controller is a JavaScript function (type/class) that is used to augment instances of In angular, a controller is a JavaScript function (type/class) that is used to augment instances of
angular {@link dev_guide.scopes Scope}, excluding the root scope. When you or angular create a new angular {@link dev_guide.scopes Scope}, excluding the root scope. When you or angular create a new
child scope object via the {@link api/angular.scope.$new scope.$new} API , there is an child scope object via the {@link api/angular.scope.$new scope.$new} API , there is an
option to pass in a controller as a method argument. This will tell angular to associate the option to pass in a controller as a method argument. This will tell angular to associate the
controller with the new scope and to augment its behavior. controller with the new scope and to augment its behavior.
Use controllers to: Use controllers to:
- Set up the initial state of a scope object. - Set up the initial state of a scope object.
- Add behavior to the scope object. - Add behavior to the scope object.
# Setting up the initial state of a scope object # Setting up the initial state of a scope object
Typically, when you create an application you need to set up an initial state for an angular scope. Typically, when you create an application you need to set up an initial state for an angular scope.
Angular applies (in the sense of JavaScript's `Function#apply`) the controller constructor function Angular applies (in the sense of JavaScript's `Function#apply`) the controller constructor function
to a new angular scope object, which sets up an initial scope state. This means that angular never to a new angular scope object, which sets up an initial scope state. This means that angular never
creates instances of the controller type (by invoking the `new` operator on the controller creates instances of the controller type (by invoking the `new` operator on the controller
constructor). Constructors are always applied to an existing scope object. constructor). Constructors are always applied to an existing scope object.
You set up the initial state of a scope by creating model properties. For example: You set up the initial state of a scope by creating model properties. For example:
function GreetingCtrl() { function GreetingCtrl() {
this.greeting = 'Hola!'; this.greeting = 'Hola!';
} }
The `GreetingCtrl` controller creates a `greeting` model which can be referred to in a template. The `GreetingCtrl` controller creates a `greeting` model which can be referred to in a template.
When a controller function is applied to an angular scope object, the `this` of the controller When a controller function is applied to an angular scope object, the `this` of the controller
function becomes the scope of the angular scope object, so any assignment to `this` within the function becomes the scope of the angular scope object, so any assignment to `this` within the
controller function happens on the angular scope object. controller function happens on the angular scope object.
# Adding Behavior to a Scope Object # Adding Behavior to a Scope Object
Behavior on an angular scope object is in the form of scope method properties available to the Behavior on an angular scope object is in the form of scope method properties available to the
template/view. This behavior interacts with and modifies the application model. template/view. This behavior interacts with and modifies the application model.
As discussed in the {@link dev_guide.mvc.understanding_model Model} section of this guide, any As discussed in the {@link dev_guide.mvc.understanding_model Model} section of this guide, any
objects (or primitives) assigned to the scope become model properties. Any functions assigned to objects (or primitives) assigned to the scope become model properties. Any functions assigned to
the scope, along with any prototype methods of the controller type, become functions available in the scope, along with any prototype methods of the controller type, become functions available in
@ -63,24 +50,18 @@ the `this` keyword of any controller method is always bound to the scope that th
augments). This is how the second task of adding behavior to the scope is accomplished. augments). This is how the second task of adding behavior to the scope is accomplished.
# Using Controllers Correctly # Using Controllers Correctly
In general, a controller shouldn't try to do too much. It should contain only the business logic In general, a controller shouldn't try to do too much. It should contain only the business logic
needed for a single view. needed for a single view.
The most common way to keep controllers slim is by encapsulating work that doesn't belong to The most common way to keep controllers slim is by encapsulating work that doesn't belong to
controllers into services and then using these services in controllers via dependency injection. controllers into services and then using these services in controllers via dependency injection.
This is discussed in the {@link dev_guide.di Dependency Injection} {@link dev_guide.services This is discussed in the {@link dev_guide.di Dependency Injection} {@link dev_guide.services
Services} sections of this guide. Services} sections of this guide.
Do not use controllers for: Do not use controllers for:
- Any kind of DOM manipulation — Controllers should contain only business logic. DOM - Any kind of DOM manipulation — Controllers should contain only business logic. DOM
manipulation—the presentation logic of an application—is well known for being hard to test. manipulation—the presentation logic of an application—is well known for being hard to test.
Putting any presentation logic into controllers significantly affects testability of the business Putting any presentation logic into controllers significantly affects testability of the business
@ -95,40 +76,29 @@ services} instead.
instances). instances).
# Associating Controllers with Angular Scope Objects # Associating Controllers with Angular Scope Objects
You can associate controllers with scope objects explicitly via the {@link api/angular.scope.$new You can associate controllers with scope objects explicitly via the {@link api/angular.scope.$new
scope.$new} api or implicitly via the {@link api/angular.directive.ng:controller ng:controller scope.$new} api or implicitly via the {@link api/angular.directive.ng:controller ng:controller
directive} or {@link api/angular.service.$route $route service}. directive} or {@link api/angular.service.$route $route service}.
## Controller Constructor and Methods Example ## Controller Constructor and Methods Example
To illustrate how the controller component works in angular, let's create a little app with the To illustrate how the controller component works in angular, let's create a little app with the
following components: following components:
- A {@link dev_guide.templates template} with two buttons and a simple message - A {@link dev_guide.templates template} with two buttons and a simple message
- A model consisting of a string named `spice` - A model consisting of a string named `spice`
- A controller with two functions that set the value of `spice` - A controller with two functions that set the value of `spice`
The message in our template contains a binding to the `spice` model, which by default is set to the The message in our template contains a binding to the `spice` model, which by default is set to the
string "very". Depending on which button is clicked, the `spice` model is set to `chili` or string "very". Depending on which button is clicked, the `spice` model is set to `chili` or
`jalapeño`, and the message is automatically updated by data-binding. `jalapeño`, and the message is automatically updated by data-binding.
## A Spicy Controller Example ## A Spicy Controller Example
<pre> <pre>
<body ng:controller="SpicyCtrl"> <body ng:controller="SpicyCtrl">
<button ng:click="chiliSpicy()">Chili</button> <button ng:click="chiliSpicy()">Chili</button>
@ -136,7 +106,6 @@ string "very". Depending on which button is clicked, the `spice` model is set to
<p>The food is {{spice}} spicy!</p> <p>The food is {{spice}} spicy!</p>
</body> </body>
function SpicyCtrl() { function SpicyCtrl() {
this.spice = 'very'; this.spice = 'very';
this.chiliSpicy = function() { this.chiliSpicy = function() {
@ -144,16 +113,13 @@ function SpicyCtrl() {
} }
} }
SpicyCtrl.prototype.jalapenoSpicy = function() { SpicyCtrl.prototype.jalapenoSpicy = function() {
this.spice = 'jalapeño'; this.spice = 'jalapeño';
} }
</pre> </pre>
Things to notice in the example above: Things to notice in the example above:
- The `ng:controller` directive is used to (implicitly) create a scope for our template, and the - The `ng:controller` directive is used to (implicitly) create a scope for our template, and the
scope is augmented (managed) by the `SpicyCtrl` controller. scope is augmented (managed) by the `SpicyCtrl` controller.
- `SpicyCtrl` is just a plain JavaScript function. As an (optional) naming convention the name - `SpicyCtrl` is just a plain JavaScript function. As an (optional) naming convention the name
@ -166,14 +132,11 @@ as prototype methods of the controller constructor function (the `jalapenoSpicy`
- Both controller methods are available in the template (for the `body` element and and its - Both controller methods are available in the template (for the `body` element and and its
children). children).
Controller methods can also take arguments, as demonstrated in the following variation of the Controller methods can also take arguments, as demonstrated in the following variation of the
previous example. previous example.
## Controller Method Arguments Example ## Controller Method Arguments Example
<pre> <pre>
<body ng:controller="SpicyCtrl"> <body ng:controller="SpicyCtrl">
<input name="customSpice" value="wasabi"> <input name="customSpice" value="wasabi">
@ -182,7 +145,6 @@ previous example.
<p>The food is {{spice}} spicy!</p> <p>The food is {{spice}} spicy!</p>
</body> </body>
function SpicyCtrl() { function SpicyCtrl() {
this.spice = 'very'; this.spice = 'very';
this.spicy = function(spice) { this.spicy = function(spice) {
@ -191,22 +153,17 @@ function SpicyCtrl() {
} }
</pre> </pre>
Notice that the `SpicyCtrl` controller now defines just one method called `spicy`, which takes one Notice that the `SpicyCtrl` controller now defines just one method called `spicy`, which takes one
argument called `spice`. The template then refers to this controller method and passes in a string argument called `spice`. The template then refers to this controller method and passes in a string
constant `'chili'` in the binding for the first button and a model property `spice` (bound to an constant `'chili'` in the binding for the first button and a model property `spice` (bound to an
input box) in the second button. input box) in the second button.
## Controller Inheritance Example ## Controller Inheritance Example
Controller inheritance in angular is based on {@link api/angular.scope Scope} inheritance. Let's Controller inheritance in angular is based on {@link api/angular.scope Scope} inheritance. Let's
have a look at an example: have a look at an example:
<pre> <pre>
<body ng:controller="MainCtrl"> <body ng:controller="MainCtrl">
<p>Good {{timeOfDay}}, {{name}}!</p> <p>Good {{timeOfDay}}, {{name}}!</p>
@ -215,29 +172,24 @@ have a look at an example:
<p ng:controller="BabyCtrl">Good {{timeOfDay}}, {{name}}!</p> <p ng:controller="BabyCtrl">Good {{timeOfDay}}, {{name}}!</p>
</body> </body>
function MainCtrl() { function MainCtrl() {
this.timeOfDay = 'morning'; this.timeOfDay = 'morning';
this.name = 'Nikki'; this.name = 'Nikki';
} }
function ChildCtrl() { function ChildCtrl() {
this.name = 'Mattie'; this.name = 'Mattie';
} }
function BabyCtrl() { function BabyCtrl() {
this.timeOfDay = 'evening'; this.timeOfDay = 'evening';
this.name = 'Gingerbreak Baby'; this.name = 'Gingerbreak Baby';
} }
</pre> </pre>
Notice how we nested three `ng:controller` directives in our template. This template construct will Notice how we nested three `ng:controller` directives in our template. This template construct will
result in 4 scopes being created for our view: result in 4 scopes being created for our view:
- The root scope - The root scope
- The `MainCtrl` scope, which contains `timeOfDay` and `name` models - The `MainCtrl` scope, which contains `timeOfDay` and `name` models
- The `ChildCtrl` scope, which shadows the `name` model from the previous scope and inherits the - The `ChildCtrl` scope, which shadows the `name` model from the previous scope and inherits the
@ -245,28 +197,21 @@ result in 4 scopes being created for our view:
- The `BabyCtrl` scope, which shadows both the `timeOfDay` model defined in `MainCtrl` and `name` - The `BabyCtrl` scope, which shadows both the `timeOfDay` model defined in `MainCtrl` and `name`
model defined in the ChildCtrl model defined in the ChildCtrl
Inheritance works between controllers in the same way as it does with models. So in our previous Inheritance works between controllers in the same way as it does with models. So in our previous
examples, all of the models could be replaced with controller methods that return string values. examples, all of the models could be replaced with controller methods that return string values.
Note: Standard prototypical inheritance between two controllers doesn't work as one might expect, Note: Standard prototypical inheritance between two controllers doesn't work as one might expect,
because as we mentioned earlier, controllers are not instantiated directly by angular, but rather because as we mentioned earlier, controllers are not instantiated directly by angular, but rather
are applied to the scope object. are applied to the scope object.
## Testing Controllers ## Testing Controllers
The way to test a controller depends upon how complicated the controller is. The way to test a controller depends upon how complicated the controller is.
- If your controller doesn't use DI or scope methods — create the controller with the `new` - If your controller doesn't use DI or scope methods — create the controller with the `new`
operator and test away. For example: operator and test away. For example:
Controller Function: Controller Function:
<pre> <pre>
function myController() { function myController() {
@ -274,31 +219,25 @@ function myController() {
{"name":"jalapeno", "spiceiness":"hot hot hot!"}, {"name":"jalapeno", "spiceiness":"hot hot hot!"},
{"name":"habanero", "spiceness":"LAVA HOT!!"}]; {"name":"habanero", "spiceness":"LAVA HOT!!"}];
this.spice = "habanero"; this.spice = "habanero";
} }
</pre> </pre>
Controller Test: Controller Test:
<pre> <pre>
describe('myController function', function() { describe('myController function', function() {
describe('myController', function(){ describe('myController', function(){
var ctrl; var ctrl;
beforeEach(function() { beforeEach(function() {
ctrl = new myController(); ctrl = new myController();
}); });
it('should create "spices" model with 3 spices', function() { it('should create "spices" model with 3 spices', function() {
expect(ctrl.spices.length).toBe(3); expect(ctrl.spices.length).toBe(3);
}); });
it('should set the default value of spice', function() { it('should set the default value of spice', function() {
expect(ctrl.spice).toBe('habanero'); expect(ctrl.spice).toBe('habanero');
}); });
@ -306,18 +245,16 @@ describe('myController function', function() {
}); });
</pre> </pre>
- If your controller does use DI or scope methods — create a root scope, then create the controller - If your controller does use DI or scope methods — create a root scope, then create the controller
in the root scope with `scope.$new(MyController)`. Test the controller using `$eval`, if necessary. in the root scope with `scope.$new(MyController)`. Test the controller using `$eval`, if necessary.
- If you need to test a nested controller that depends on its parent's state — create a root scope, - If you need to test a nested controller that depends on its parent's state — create a root scope,
create a parent scope, create a child scope, and test the controller using $eval if necessary. create a parent scope, create a child scope, and test the controller using $eval if necessary.
## Related Topics ## Related Topics
* {@link dev_guide.mvc About MVC in Angular} * {@link dev_guide.mvc About MVC in Angular}
* {@link dev_guide.mvc.understanding_model Understanding the Model Component} * {@link dev_guide.mvc.understanding_model Understanding the Model Component}
* {@link dev_guide.mvc.understanding_view Understanding the View Component} * {@link dev_guide.mvc.understanding_view Understanding the View Component}

View file

@ -3,94 +3,70 @@
@name Developer Guide: About MVC in Angular: Understanding the Model Component @name Developer Guide: About MVC in Angular: Understanding the Model Component
@description @description
Depending on the context of the discussion in angular documentation, the term _model_ can refer to Depending on the context of the discussion in angular documentation, the term _model_ can refer to
either a single object representing one entity (for example, a model called "phones" with its value either a single object representing one entity (for example, a model called "phones" with its value
being an array of phones) or the entire data model for the application (all entities). being an array of phones) or the entire data model for the application (all entities).
In angular, a model is any data that is reachable as a property of an angular {@link In angular, a model is any data that is reachable as a property of an angular {@link
dev_guide.scopes Scope} object. The name of the property is the model identifier and the value is dev_guide.scopes Scope} object. The name of the property is the model identifier and the value is
any JavaScript object (including arrays and primitives). any JavaScript object (including arrays and primitives).
The only requirement for a JavaScript object to be a model in angular is that the object must be The only requirement for a JavaScript object to be a model in angular is that the object must be
referenced by an angular scope as a property of that scope object. This property reference can be referenced by an angular scope as a property of that scope object. This property reference can be
created explicitly or implicitly. created explicitly or implicitly.
You can create models by explicitly creating scope properties referencing JavaScript objects in the You can create models by explicitly creating scope properties referencing JavaScript objects in the
following ways: following ways:
* Make a direct property assignment to the scope object in JavaScript code; this most commonly * Make a direct property assignment to the scope object in JavaScript code; this most commonly
occurs in controllers: occurs in controllers:
function MyCtrl() { function MyCtrl() {
// create property 'foo' on the MyCtrl's scope // create property 'foo' on the MyCtrl's scope
// and assign it an initial value 'bar' // and assign it an initial value 'bar'
this.foo = 'bar'; this.foo = 'bar';
} }
* Use an {@link dev_guide.expressions angular expression} with an assignment operator in templates: * Use an {@link dev_guide.expressions angular expression} with an assignment operator in templates:
<button ng:click="{{foos='ball'}}">Click me</button> <button ng:click="{{foos='ball'}}">Click me</button>
* Use {@link api/angular.directive.ng:init ng:init directive} in templates (for toy/example apps * Use {@link api/angular.directive.ng:init ng:init directive} in templates (for toy/example apps
only, not recommended for real applications): only, not recommended for real applications):
<body ng:init=" foo = 'bar' "> <body ng:init=" foo = 'bar' ">
Angular creates models implicitly (by creating a scope property and assigning it a suitable value) Angular creates models implicitly (by creating a scope property and assigning it a suitable value)
when processing the following template constructs: when processing the following template constructs:
* Form input, select, and textarea elements: * Form input, select, and textarea elements:
<input name="query" value="fluffy cloud"> <input name="query" value="fluffy cloud">
The code above creates a model called "query" on the current scope with the value set to "fluffy The code above creates a model called "query" on the current scope with the value set to "fluffy
cloud". cloud".
* An iterator declaration in {@link api/angular.widget.@ng:repeat ng:repeater}: * An iterator declaration in {@link api/angular.widget.@ng:repeat ng:repeater}:
<p ng:repeat="phone in phones"></p> <p ng:repeat="phone in phones"></p>
The code above creates one child scope for each item in the "phones" array and creates a "phone" The code above creates one child scope for each item in the "phones" array and creates a "phone"
object (model) on each of these scopes with its value set to the value of "phone" in the array. object (model) on each of these scopes with its value set to the value of "phone" in the array.
In angular, a JavaScript object stops being a model when: In angular, a JavaScript object stops being a model when:
* No angular scope contains a property that references the object. * No angular scope contains a property that references the object.
* All angular scopes that contain a property referencing the object become stale and eligible for * All angular scopes that contain a property referencing the object become stale and eligible for
garbage collection. garbage collection.
The following illustration shows a simple data model created implicitly from a simple template: The following illustration shows a simple data model created implicitly from a simple template:
<img src="img/guide/about_model_final.png"> <img src="img/guide/about_model_final.png">
## Related Topics ## Related Topics
* {@link dev_guide.mvc About MVC in Angular} * {@link dev_guide.mvc About MVC in Angular}
* {@link dev_guide.mvc.understanding_controller Understanding the Controller Component} * {@link dev_guide.mvc.understanding_controller Understanding the Controller Component}
* {@link dev_guide.mvc.understanding_view Understanding the View Component} * {@link dev_guide.mvc.understanding_view Understanding the View Component}

View file

@ -3,14 +3,11 @@
@name Developer Guide: About MVC in Angular: Understanding the View Component @name Developer Guide: About MVC in Angular: Understanding the View Component
@description @description
In angular, the view is the DOM loaded and rendered in the browser, after angular has transformed In angular, the view is the DOM loaded and rendered in the browser, after angular has transformed
the DOM based on information in the template, controller and model. the DOM based on information in the template, controller and model.
<img src="img/guide/about_view_final.png"> <img src="img/guide/about_view_final.png">
In the angular implementation of MVC, the view has knowledge of both the model and the controller. In the angular implementation of MVC, the view has knowledge of both the model and the controller.
The view knows about the model where two-way data-binding occurs. The view has knowledge of the The view knows about the model where two-way data-binding occurs. The view has knowledge of the
controller through angular directives, such as {@link api/angular.directive.ng:controller controller through angular directives, such as {@link api/angular.directive.ng:controller
@ -19,11 +16,8 @@ ng:controller} and {@link api/angular.widget.ng:view ng:view}, and through bindi
controller function. controller function.
## Related Topics ## Related Topics
* {@link dev_guide.mvc About MVC in Angular} * {@link dev_guide.mvc About MVC in Angular}
* {@link dev_guide.mvc.understanding_model Understanding the Model Component} * {@link dev_guide.mvc.understanding_model Understanding the Model Component}
* {@link dev_guide.mvc.understanding_controller Understanding the Controller Component} * {@link dev_guide.mvc.understanding_controller Understanding the Controller Component}

View file

@ -3,33 +3,25 @@
@description @description
# What Is Angular? # What Is Angular?
The short answer: angular is a new, powerful, client-side technology that makes it much easier for The short answer: angular is a new, powerful, client-side technology that makes it much easier for
you to create dynamic web sites and complex web apps, all without leaving the comfort of your HTML you to create dynamic web sites and complex web apps, all without leaving the comfort of your HTML
/ JavaScript home. / JavaScript home.
The long answer: it depends on where you're coming from... The long answer: it depends on where you're coming from...
* If you're a web designer, you might perceive angular to be a sweet {@link dev_guide.templates * If you're a web designer, you might perceive angular to be a sweet {@link dev_guide.templates
templating} system, that doesn't get in your way and provides you with lots of nice built-ins that templating} system, that doesn't get in your way and provides you with lots of nice built-ins that
make it easier to do what you want to do. make it easier to do what you want to do.
* If you're a web developer, you might be thrilled that angular functions as an excellent web * If you're a web developer, you might be thrilled that angular functions as an excellent web
framework, one that assists you all the way through the development cycle. framework, one that assists you all the way through the development cycle.
* If you want to go deeper, you can immerse yourself in angular's extensible HTML {@link * If you want to go deeper, you can immerse yourself in angular's extensible HTML {@link
dev_guide.compiler compiler} that runs in your browser. The angular compiler teaches your browser dev_guide.compiler compiler} that runs in your browser. The angular compiler teaches your browser
new tricks. new tricks.
Angular is not just a templating system, but you can create fantastic templates with it. Angular is Angular is not just a templating system, but you can create fantastic templates with it. Angular is
not just a web framework, but it features a very nice framework. Angular is not just an extensible not just a web framework, but it features a very nice framework. Angular is not just an extensible
HTML compiler, but the compiler is at the core of Angular. Angular includes all of these HTML compiler, but the compiler is at the core of Angular. Angular includes all of these
@ -37,22 +29,17 @@ components, along with others. Angular is far greater than the sum of its parts.
better way to develop web applications! better way to develop web applications!
## An Introductory Angular Example ## An Introductory Angular Example
Let's say that you are a web designer, and you've spent many thous — erm, hundreds of hours Let's say that you are a web designer, and you've spent many thous — erm, hundreds of hours
designing web sites. But at this point, the thought of manipulating the DOM, writing listeners and designing web sites. But at this point, the thought of manipulating the DOM, writing listeners and
input validators, all just to implement a simple form? No. You either don't want to go there in input validators, all just to implement a simple form? No. You either don't want to go there in
the first place or you've been there and the thrill is gone. the first place or you've been there and the thrill is gone.
So look over the following simple example written using angular. Note that it features only the So look over the following simple example written using angular. Note that it features only the
templating aspect of angular, but this should suffice for now to quickly demonstrate how much templating aspect of angular, but this should suffice for now to quickly demonstrate how much
easier a web developer's life can if they're using angular: easier a web developer's life can if they're using angular:
<doc:example> <doc:example>
<doc:source> <doc:source>
<b>Invoice:</b> <b>Invoice:</b>
@ -85,43 +72,32 @@ ng:required/></td>
--> -->
</doc:example> </doc:example>
Try out the Live Preview above, and then let's walk through the example and describe what's going Try out the Live Preview above, and then let's walk through the example and describe what's going
on. on.
In the `<html>` tag, we add an attribute to let the browser know about the angular namespace: In the `<html>` tag, we add an attribute to let the browser know about the angular namespace:
<html xmlns:ng="http://angularjs.org"> <html xmlns:ng="http://angularjs.org">
This ensures angular runs nicely in all major browsers. This ensures angular runs nicely in all major browsers.
In the `<script>` tag we do two angular setup tasks: In the `<script>` tag we do two angular setup tasks:
1. We load `angular.js`. 1. We load `angular.js`.
2. The angular {@link api/angular.directive.ng:autobind ng:autobind} directive tells angular to 2. The angular {@link api/angular.directive.ng:autobind ng:autobind} directive tells angular to
{@link dev_guide.compiler compile} and manage the whole HTML document. {@link dev_guide.compiler compile} and manage the whole HTML document.
`<script src="http://code.angularjs.org/0.9.15/angular-0.9.15.min.js" `<script src="http://code.angularjs.org/0.9.15/angular-0.9.15.min.js"
ng:autobind></script>` ng:autobind></script>`
From the `name` attribute of the `<input>` tags, angular automatically sets up two-way data From the `name` attribute of the `<input>` tags, angular automatically sets up two-way data
binding, and we also demonstrate some easy input validation: binding, and we also demonstrate some easy input validation:
Quantity: <input name="qty" value="1" ng:validate="integer:0" ng:required/> Quantity: <input name="qty" value="1" ng:validate="integer:0" ng:required/>
Cost: <input name="cost" value="199.95" ng:validate="number" ng:required/> Cost: <input name="cost" value="199.95" ng:validate="number" ng:required/>
These input widgets look normal enough, but consider these points: These input widgets look normal enough, but consider these points:
* When this page loaded, angular bound the names of the input widgets (`qty` and `cost`) to * When this page loaded, angular bound the names of the input widgets (`qty` and `cost`) to
variables of the same name. Think of those variables as the "Model" component of the variables of the same name. Think of those variables as the "Model" component of the
Model-View-Controller design pattern. Model-View-Controller design pattern.
@ -131,13 +107,10 @@ or leave the the input fields blank, the borders turn red color, and the display
These `ng:` directives make it easier to implement field validators than coding them in JavaScript, These `ng:` directives make it easier to implement field validators than coding them in JavaScript,
no? Yes. no? Yes.
And finally, the mysterious `{{ double curly braces }}`: And finally, the mysterious `{{ double curly braces }}`:
Total: {{qty * cost | currency}} Total: {{qty * cost | currency}}
This notation, `{{ _expression_ }}`, is a bit of built-in angular {@link dev_guide.compiler.markup This notation, `{{ _expression_ }}`, is a bit of built-in angular {@link dev_guide.compiler.markup
markup}, a shortcut for displaying data to the user. The expression within curly braces gets markup}, a shortcut for displaying data to the user. The expression within curly braces gets
transformed by the angular compiler into an angular directive ({@link api/angular.directive.ng:bind transformed by the angular compiler into an angular directive ({@link api/angular.directive.ng:bind
@ -145,36 +118,28 @@ ng:bind}). The expression itself can be a combination of both an expression and
dev_guide.templates.filters filter}: `{{ expression | filter }}`. Angular provides filters for dev_guide.templates.filters filter}: `{{ expression | filter }}`. Angular provides filters for
formatting display data. formatting display data.
In the example above, the expression in double-curly braces directs angular to, "Bind the data we In the example above, the expression in double-curly braces directs angular to, "Bind the data we
got from the input widgets to the display, multiply them together, and format the resulting number got from the input widgets to the display, multiply them together, and format the resulting number
into output that looks like money." into output that looks like money."
# The Angular Philosophy # The Angular Philosophy
Angular is built around the belief that declarative code is better than imperative when it comes to Angular is built around the belief that declarative code is better than imperative when it comes to
building UIs and wiring software components together, while imperative code is excellent for building UIs and wiring software components together, while imperative code is excellent for
expressing business logic. expressing business logic.
Not to put too fine a point on it, but if you wanted to add a new label to your application, you Not to put too fine a point on it, but if you wanted to add a new label to your application, you
could do so by simply adding text to the HTML template, saving the code, and refreshing your could do so by simply adding text to the HTML template, saving the code, and refreshing your
browser: browser:
<pre> <pre>
<span class="label">Hello</span> <span class="label">Hello</span>
</pre> </pre>
Or, as in programmatic systems (like {@link http://code.google.com/webtoolkit/ GWT}), you would Or, as in programmatic systems (like {@link http://code.google.com/webtoolkit/ GWT}), you would
have to write the code and then run the code like this: have to write the code and then run the code like this:
<pre> <pre>
var label = new Label(); var label = new Label();
label.setText('Hello'); label.setText('Hello');
@ -182,15 +147,11 @@ label.setClass('label');
parent.addChild(label); parent.addChild(label);
</pre> </pre>
That's one line of markup versus four times as much code. That's one line of markup versus four times as much code.
## More Angular Philosophy ## More Angular Philosophy
* It is a very good idea to decouple DOM manipulation from app logic. This dramatically improves * It is a very good idea to decouple DOM manipulation from app logic. This dramatically improves
the testability of the code. the testability of the code.
* It is a really, _really_ good idea to regard app testing as equal in importance to app writing. * It is a really, _really_ good idea to regard app testing as equal in importance to app writing.
@ -201,11 +162,9 @@ development work to progress in parallel, and allows for reuse of both sides.
building an app: from designing the UI, through writing the business logic, to testing. building an app: from designing the UI, through writing the business logic, to testing.
* It is always good to make common tasks trivial and difficult tasks possible. * It is always good to make common tasks trivial and difficult tasks possible.
Now that we're homing in on what angular is, perhaps now would be a good time to list a few things Now that we're homing in on what angular is, perhaps now would be a good time to list a few things
that angular is not: that angular is not:
* It's not a Library. You don't just call its functions, although it does provide you with some * It's not a Library. You don't just call its functions, although it does provide you with some
utility APIs. utility APIs.
* It's not a DOM Manipulation Library. Angular uses jQuery to manipulate the DOM behind the scenes, * It's not a DOM Manipulation Library. Angular uses jQuery to manipulate the DOM behind the scenes,
@ -225,14 +184,10 @@ changes to the model are automatically reflected in the view. Any changes by the
are automatically reflected in the model. are automatically reflected in the model.
# Why You Want Angular # Why You Want Angular
Angular frees you from the following pain: Angular frees you from the following pain:
* **Registering callbacks:** Registering callbacks clutters your code, making it hard to see the * **Registering callbacks:** Registering callbacks clutters your code, making it hard to see the
forest for the trees. Removing common boilerplate code such as callbacks is a good thing. It vastly forest for the trees. Removing common boilerplate code such as callbacks is a good thing. It vastly
reduces the amount of JavaScript coding _you_ have to do, and it makes it easier to see what your reduces the amount of JavaScript coding _you_ have to do, and it makes it easier to see what your
@ -256,15 +211,11 @@ get started developing features quickly. As a bonus, you get full control over t
process in automated tests. process in automated tests.
# Watch a Presentation About Angular # Watch a Presentation About Angular
Here is an early presentation on angular, but note that substantial development has occurred since Here is an early presentation on angular, but note that substantial development has occurred since
the talk was given in July of 2010. the talk was given in July of 2010.
<object width="480" height="385"> <object width="480" height="385">
<param name="movie" value="http://www.youtube.com/v/elvcgVSynRg&amp;hl=en_US&amp;fs=1"></param> <param name="movie" value="http://www.youtube.com/v/elvcgVSynRg&amp;hl=en_US&amp;fs=1"></param>
<param name="allowFullScreen" value="true"></param> <param name="allowFullScreen" value="true"></param>
@ -274,7 +225,6 @@ the talk was given in July of 2010.
allowfullscreen="true" width="480" height="385"></embed> allowfullscreen="true" width="480" height="385"></embed>
</object> </object>
{@link {@link
https://docs.google.com/present/edit?id=0Abz6S2TvsDWSZDQ0OWdjaF8yNTRnODczazdmZg&hl=en&authkey=CO-b7oID https://docs.google.com/present/edit?id=0Abz6S2TvsDWSZDQ0OWdjaF8yNTRnODczazdmZg&hl=en&authkey=CO-b7oID

View file

@ -3,48 +3,37 @@
@name Developer Guide: Scopes: Applying Controllers to Scopes @name Developer Guide: Scopes: Applying Controllers to Scopes
@description @description
When a controller function is applied to a scope, the scope is augmented with the behavior defined When a controller function is applied to a scope, the scope is augmented with the behavior defined
in the controller. The end result is that the scope behaves as if it were the controller: in the controller. The end result is that the scope behaves as if it were the controller:
<pre> <pre>
var scope = angular.scope(); var scope = angular.scope();
scope.salutation = 'Hello'; scope.salutation = 'Hello';
scope.name = 'World'; scope.name = 'World';
expect(scope.greeting).toEqual(undefined); expect(scope.greeting).toEqual(undefined);
scope.$watch('name', function(){ scope.$watch('name', function(){
this.greeting = this.salutation + ' ' + this.name + '!'; this.greeting = this.salutation + ' ' + this.name + '!';
}); });
expect(scope.greeting).toEqual('Hello World!'); expect(scope.greeting).toEqual('Hello World!');
scope.name = 'Misko'; scope.name = 'Misko';
// scope.$eval() will propagate the change to listeners // scope.$eval() will propagate the change to listeners
expect(scope.greeting).toEqual('Hello World!'); expect(scope.greeting).toEqual('Hello World!');
scope.$eval(); scope.$eval();
expect(scope.greeting).toEqual('Hello Misko!'); expect(scope.greeting).toEqual('Hello Misko!');
</pre> </pre>
## Related Topics ## Related Topics
* {@link dev_guide.scopes Angular Scope Objects} * {@link dev_guide.scopes Angular Scope Objects}
* {@link dev_guide.scopes.understanding_scopes Understanding Angular Scopes} * {@link dev_guide.scopes.understanding_scopes Understanding Angular Scopes}
* {@link dev_guide.scopes.working_scopes Working With Angular Scopes} * {@link dev_guide.scopes.working_scopes Working With Angular Scopes}
* {@link dev_guide.scopes.updating_scopes Updating Angular Scopes} * {@link dev_guide.scopes.updating_scopes Updating Angular Scopes}
## Related API ## Related API
* {@link api/angular.scope Angular Scope API} * {@link api/angular.scope Angular Scope API}

View file

@ -4,48 +4,35 @@
@description @description
An angular scope is a JavaScript type defined by angular. Instances of this type are objects that An angular scope is a JavaScript type defined by angular. Instances of this type are objects that
serve as the context within which all model and controller methods live and get evaluated. serve as the context within which all model and controller methods live and get evaluated.
Angular links scope objects to specific points in a compiled (processed) template. This linkage Angular links scope objects to specific points in a compiled (processed) template. This linkage
provides the contexts in which angular creates data-bindings between the model and the view. You provides the contexts in which angular creates data-bindings between the model and the view. You
can think of angular scope objects as the medium through which the model, view, and controller can think of angular scope objects as the medium through which the model, view, and controller
communicate. communicate.
In addition to providing the context in which data is evaluated, angular scope objects watch for In addition to providing the context in which data is evaluated, angular scope objects watch for
model changes. The scope objects also notify all components interested in any model changes (for model changes. The scope objects also notify all components interested in any model changes (for
example, functions registered through {@link api/angular.scope.$watch $watch}, bindings created by example, functions registered through {@link api/angular.scope.$watch $watch}, bindings created by
{@link api/angular.directive.ng:bind ng:bind}, or HTML input elements). {@link api/angular.directive.ng:bind ng:bind}, or HTML input elements).
Angular scope objects are responsible for: Angular scope objects are responsible for:
* Gluing the model, controller and view template together. * Gluing the model, controller and view template together.
* Providing the mechanism to watch for model changes ({@link api/angular.scope.$watch}). * Providing the mechanism to watch for model changes ({@link api/angular.scope.$watch}).
* Notifying interested components when the model changes ({@link api/angular.scope.$eval}). * Notifying interested components when the model changes ({@link api/angular.scope.$eval}).
* Providing the context in which all controller functions and angular expressions are evaluated. * Providing the context in which all controller functions and angular expressions are evaluated.
## Related Topics ## Related Topics
* {@link dev_guide.scopes.understanding_scopes Understanding Scopes} * {@link dev_guide.scopes.understanding_scopes Understanding Scopes}
* {@link dev_guide.scopes.working_scopes Working With Scopes} * {@link dev_guide.scopes.working_scopes Working With Scopes}
* {@link dev_guide.scopes.controlling_scopes Applying Controllers to Scopes} * {@link dev_guide.scopes.controlling_scopes Applying Controllers to Scopes}
* {@link dev_guide.scopes.updating_scopes Updating Scopes} * {@link dev_guide.scopes.updating_scopes Updating Scopes}
## Related API ## Related API
* {@link api/angular.scope Angular Scope API} * {@link api/angular.scope Angular Scope API}

View file

@ -3,7 +3,6 @@
@name Developer Guide: Scopes: Understanding Scopes @name Developer Guide: Scopes: Understanding Scopes
@description @description
Angular automatically creates a root scope during initialization, and attaches it to the page's Angular automatically creates a root scope during initialization, and attaches it to the page's
root DOM element (usually `<html>`). The root scope object, along with any of its child scope root DOM element (usually `<html>`). The root scope object, along with any of its child scope
objects, serves as the infrastructure on which your data model is built. The data model (JavaScript objects, serves as the infrastructure on which your data model is built. The data model (JavaScript
@ -11,20 +10,16 @@ objects, arrays, or primitives) is attached to angular scope properties. Angular
values to the DOM where bindings are specified in the template. Angular attaches any controller values to the DOM where bindings are specified in the template. Angular attaches any controller
functions you have created to their respective scope objects. functions you have created to their respective scope objects.
<img src="img/guide/simple_scope_final.png"> <img src="img/guide/simple_scope_final.png">
Angular scopes can be nested, so a child scope has a parent scope upstream in the DOM. When you Angular scopes can be nested, so a child scope has a parent scope upstream in the DOM. When you
display an angular expression in the view, angular walks the DOM tree looking in the closest display an angular expression in the view, angular walks the DOM tree looking in the closest
attached scope object for the specified data. If it doesn't find the data in the closest attached attached scope object for the specified data. If it doesn't find the data in the closest attached
scope, it looks further up the scope hierarchy until it finds the data. scope, it looks further up the scope hierarchy until it finds the data.
A child scope object inherits properties from its parents. For example, in the following snippet of A child scope object inherits properties from its parents. For example, in the following snippet of
code, observe how the value of `name` changes, based on the HTML element it is displayed in: code, observe how the value of `name` changes, based on the HTML element it is displayed in:
<doc:example> <doc:example>
<doc:source> <doc:source>
<ul ng:init="name='Hank'; names=['Igor', 'Misko', 'Gail', 'Kai']"> <ul ng:init="name='Hank'; names=['Igor', 'Misko', 'Gail', 'Kai']">
@ -41,7 +36,6 @@ code, observe how the value of `name` changes, based on the HTML element it is d
expect(using('.doc-example-live').repeater('li').row(1)). expect(using('.doc-example-live').repeater('li').row(1)).
toEqual(['Misko']); toEqual(['Misko']);
expect(using('.doc-example-live').repeater('li').row(2)). expect(using('.doc-example-live').repeater('li').row(2)).
toEqual(['Gail']); toEqual(['Gail']);
expect(using('.doc-example-live').repeater('li').row(3)). expect(using('.doc-example-live').repeater('li').row(3)).
@ -52,32 +46,24 @@ code, observe how the value of `name` changes, based on the HTML element it is d
</doc:scenario> </doc:scenario>
</doc:example> </doc:example>
The angular {@link api/angular.widget.@ng:repeat ng:repeat} directive creates a new scope for each The angular {@link api/angular.widget.@ng:repeat ng:repeat} directive creates a new scope for each
element that it repeats (in this example the elements are list items). In the `<ul>` element, we element that it repeats (in this example the elements are list items). In the `<ul>` element, we
initialized `name` to "Hank", and we created an array called `names` to use as the data source for initialized `name` to "Hank", and we created an array called `names` to use as the data source for
the list items. In each `<li>` element, `name` is overridden. Outside of the `<li>` repeater, the the list items. In each `<li>` element, `name` is overridden. Outside of the `<li>` repeater, the
original value of `name` is displayed. original value of `name` is displayed.
The following illustration shows the DOM and angular scopes for the example above: The following illustration shows the DOM and angular scopes for the example above:
<img src="img/guide/dom_scope_final.png"> <img src="img/guide/dom_scope_final.png">
## Related Topics ## Related Topics
* {@link dev_guide.scopes Angular Scope Objects} * {@link dev_guide.scopes Angular Scope Objects}
* {@link dev_guide.scopes.working_scopes Working With Scopes} * {@link dev_guide.scopes.working_scopes Working With Scopes}
* {@link dev_guide.scopes.controlling_scopes Applying Controllers to Scopes} * {@link dev_guide.scopes.controlling_scopes Applying Controllers to Scopes}
* {@link dev_guide.scopes.updating_scopes Updating Scopes} * {@link dev_guide.scopes.updating_scopes Updating Scopes}
## Related API ## Related API
* {@link api/angular.scope Angular Scope API} * {@link api/angular.scope Angular Scope API}

View file

@ -3,19 +3,16 @@
@name Developer Guide: Scopes: Updating Scope Properties @name Developer Guide: Scopes: Updating Scope Properties
@description @description
You can update a scope by calling its {@link api/angular.scope.$eval $eval()} method, but usually You can update a scope by calling its {@link api/angular.scope.$eval $eval()} method, but usually
you do not have to do this explicitly. In most cases, angular intercepts all external events (such you do not have to do this explicitly. In most cases, angular intercepts all external events (such
as user interactions, XHRs, and timers) and calls the `$eval()` method on the scope object for you as user interactions, XHRs, and timers) and calls the `$eval()` method on the scope object for you
at the right time. The only time you might need to call `$eval()` explicitly is when you create at the right time. The only time you might need to call `$eval()` explicitly is when you create
your own custom widget or service. your own custom widget or service.
The reason it is unnecessary to call `$eval()` from within your controller functions when you use The reason it is unnecessary to call `$eval()` from within your controller functions when you use
built-in angular widgets and services is because a change in the data model triggers a call to the built-in angular widgets and services is because a change in the data model triggers a call to the
`$eval()` method on the scope object where the data model changed. `$eval()` method on the scope object where the data model changed.
When a user inputs data, angularized widgets copy the data to the appropriate scope and then call When a user inputs data, angularized widgets copy the data to the appropriate scope and then call
the `$eval()` method on the root scope to update the view. It works this way because scopes are the `$eval()` method on the root scope to update the view. It works this way because scopes are
inherited, and a child scope `$eval()` overrides its parent's `$eval()` method. Updating the whole inherited, and a child scope `$eval()` overrides its parent's `$eval()` method. Updating the whole
@ -23,25 +20,19 @@ page requires a call to `$eval()` on the root scope as `$root.$eval()`. Similarl
to fetch data from a server is made and the response comes back, the data is written into the model to fetch data from a server is made and the response comes back, the data is written into the model
and then `$eval()` is called to push updates through to the view and any other dependents. and then `$eval()` is called to push updates through to the view and any other dependents.
A widget that creates scopes (such as {@link api/angular.widget.@ng:repeat ng:repeat}) is A widget that creates scopes (such as {@link api/angular.widget.@ng:repeat ng:repeat}) is
responsible for forwarding `$eval()` calls from the parent to those child scopes. That way, calling responsible for forwarding `$eval()` calls from the parent to those child scopes. That way, calling
`$eval()` on the root scope will update the whole page. This creates a spreadsheet-like behavior `$eval()` on the root scope will update the whole page. This creates a spreadsheet-like behavior
for your app; the bound views update immediately as the user enters data. for your app; the bound views update immediately as the user enters data.
## Related Documents ## Related Documents
* {@link dev_guide.scopes Angular Scope Objects} * {@link dev_guide.scopes Angular Scope Objects}
* {@link dev_guide.scopes.understanding_scopes Understanding Angular Scope Objects} * {@link dev_guide.scopes.understanding_scopes Understanding Angular Scope Objects}
* {@link dev_guide.scopes.working_scopes Working With Angular Scopes} * {@link dev_guide.scopes.working_scopes Working With Angular Scopes}
* {@link dev_guide.scopes.controlling_scopes Applying Controllers to Scopes} * {@link dev_guide.scopes.controlling_scopes Applying Controllers to Scopes}
## Related API ## Related API
* {@link api/angular.scope Angular Scope API} * {@link api/angular.scope Angular Scope API}

View file

@ -3,27 +3,22 @@
@name Developer Guide: Scopes: Working With Angular Scopes @name Developer Guide: Scopes: Working With Angular Scopes
@description @description
When you use {@link api/angular.directive.ng:autobind ng:autobind} to bootstrap your application, When you use {@link api/angular.directive.ng:autobind ng:autobind} to bootstrap your application,
angular creates the root scope automatically for you. If you need more control over the angular creates the root scope automatically for you. If you need more control over the
bootstrapping process, or if you need to create a root scope for a test, you can do so using the bootstrapping process, or if you need to create a root scope for a test, you can do so using the
{@link api/angular.scope angular.scope()} API. {@link api/angular.scope angular.scope()} API.
Here is a simple code snippet that demonstrates how to create a scope object, assign model Here is a simple code snippet that demonstrates how to create a scope object, assign model
properties to it, and register listeners to watch for changes to the model properties: properties to it, and register listeners to watch for changes to the model properties:
<pre> <pre>
var scope = angular.scope(); var scope = angular.scope();
scope.salutation = 'Hello'; scope.salutation = 'Hello';
scope.name = 'World'; scope.name = 'World';
// Verify that greeting is undefined // Verify that greeting is undefined
expect(scope.greeting).toEqual(undefined); expect(scope.greeting).toEqual(undefined);
// Set up the watcher... // Set up the watcher...
scope.$watch('name', function(){ scope.$watch('name', function(){
// when 'name' changes, set 'greeting'... // when 'name' changes, set 'greeting'...
@ -31,35 +26,27 @@ this.greeting = this.salutation + ' ' + this.name + '!';
} }
); );
// verify that 'greeting' was set... // verify that 'greeting' was set...
expect(scope.greeting).toEqual('Hello World!'); expect(scope.greeting).toEqual('Hello World!');
// 'name' changed! // 'name' changed!
scope.name = 'Misko'; scope.name = 'Misko';
// scope.$eval() will propagate the change to listeners // scope.$eval() will propagate the change to listeners
expect(scope.greeting).toEqual('Hello World!'); expect(scope.greeting).toEqual('Hello World!');
scope.$eval(); scope.$eval();
// verify that '$eval' propagated the change // verify that '$eval' propagated the change
expect(scope.greeting).toEqual('Hello Misko!'); expect(scope.greeting).toEqual('Hello Misko!');
</pre> </pre>
## Related Topics ## Related Topics
* {@link dev_guide.scopes Angular Scope Objects} * {@link dev_guide.scopes Angular Scope Objects}
* {@link dev_guide.scopes.understanding_scopes Understanding Scopes} * {@link dev_guide.scopes.understanding_scopes Understanding Scopes}
* {@link dev_guide.scopes.controlling_scopes Applying Controllers to Scopes} * {@link dev_guide.scopes.controlling_scopes Applying Controllers to Scopes}
* {@link dev_guide.scopes.updating_scopes Updating Scopes} * {@link dev_guide.scopes.updating_scopes Updating Scopes}
## Related API ## Related API
* {@link api/angular.scope Angular Scope API} * {@link api/angular.scope Angular Scope API}

View file

@ -3,15 +3,12 @@
@name Developer Guide: Angular Services: Creating Angular Services @name Developer Guide: Angular Services: Creating Angular Services
@description @description
While angular offers several useful services, for any nontrivial application you'll find it useful While angular offers several useful services, for any nontrivial application you'll find it useful
to write your own custom services. To do this you begin by registering a service factory function to write your own custom services. To do this you begin by registering a service factory function
that angular's DI will use to create the service object when it is needed. that angular's DI will use to create the service object when it is needed.
The `angular.service` method accepts three parameters: The `angular.service` method accepts three parameters:
- `{string} name` - Name of the service. - `{string} name` - Name of the service.
- `{function()} factory` - Factory function (called just once by DI). - `{function()} factory` - Factory function (called just once by DI).
- `{Object} config` - Configuration object with the following properties: - `{Object} config` - Configuration object with the following properties:
@ -22,23 +19,19 @@ array. Defaults to `[]`.
instantiated when angular boots. If false, the service will be lazily instantiated when it is first instantiated when angular boots. If false, the service will be lazily instantiated when it is first
requested during instantiation of a dependant. Defaults to `false`. requested during instantiation of a dependant. Defaults to `false`.
The `this` of the factory function is bound to the root scope of the angular application. The `this` of the factory function is bound to the root scope of the angular application.
All angular services participate in {@link dev_guide.di dependency injection (DI)} by registering All angular services participate in {@link dev_guide.di dependency injection (DI)} by registering
themselves with angular's DI system (injector) under a `name` (id) as well as by declaring themselves with angular's DI system (injector) under a `name` (id) as well as by declaring
dependencies which need to be provided for the factory function of the registered service. The dependencies which need to be provided for the factory function of the registered service. The
ability to swap dependencies for mocks/stubs/dummies in tests allows for services to be highly ability to swap dependencies for mocks/stubs/dummies in tests allows for services to be highly
testable. testable.
Following is an example of a very simple service. This service depends on the `$window` service Following is an example of a very simple service. This service depends on the `$window` service
(which is passed as a parameter to the factory function) and is just a function. The service simply (which is passed as a parameter to the factory function) and is just a function. The service simply
stores all notifications; after the third one, the service displays all of the notifications by stores all notifications; after the third one, the service displays all of the notifications by
window alert. window alert.
<pre> <pre>
angular.service('notify', function(win) { angular.service('notify', function(win) {
var msgs = []; var msgs = [];
@ -53,19 +46,14 @@ window alert.
</pre> </pre>
## Related Topics ## Related Topics
* {@link dev_guide.services.understanding_services Understanding Angular Services} * {@link dev_guide.services.understanding_services Understanding Angular Services}
* {@link dev_guide.services.registering_services Registering Angular Services} * {@link dev_guide.services.registering_services Registering Angular Services}
* {@link dev_guide.services.managing_dependencies Managing Service Dependencies} * {@link dev_guide.services.managing_dependencies Managing Service Dependencies}
* {@link dev_guide.services.injecting_controllers Injecting Services Into Controllers } * {@link dev_guide.services.injecting_controllers Injecting Services Into Controllers }
* {@link dev_guide.services.testing_services Testing Angular Services} * {@link dev_guide.services.testing_services Testing Angular Services}
## Related API ## Related API
* {@link api/angular.service Angular Service API} * {@link api/angular.service Angular Service API}

View file

@ -3,11 +3,9 @@
@name Developer Guide: Angular Services: Injecting Services Into Controllers @name Developer Guide: Angular Services: Injecting Services Into Controllers
@description @description
Using services as dependencies for controllers is very similar to using services as dependencies Using services as dependencies for controllers is very similar to using services as dependencies
for another service. for another service.
Since JavaScript is a dynamic language, DI can't figure out which services to inject by static Since JavaScript is a dynamic language, DI can't figure out which services to inject by static
types (like in static typed languages). Therefore, you must specify the service name by using the types (like in static typed languages). Therefore, you must specify the service name by using the
`$inject` property, which is an array containing strings with names of services to be injected. `$inject` property, which is an array containing strings with names of services to be injected.
@ -16,7 +14,6 @@ IDs matters: the order of the services in the array will be used when calling th
with injected parameters. The names of parameters in factory function don't matter, but by with injected parameters. The names of parameters in factory function don't matter, but by
convention they match the service IDs. convention they match the service IDs.
<pre> <pre>
function myController($loc, $log) { function myController($loc, $log) {
this.firstMethod = function() { this.firstMethod = function() {
@ -32,7 +29,6 @@ this.secondMethod = function() {
myController.$inject = ['$location', '$log']; myController.$inject = ['$location', '$log'];
</pre> </pre>
<doc:example> <doc:example>
<doc:source> <doc:source>
<script type="text/javascript"> <script type="text/javascript">
@ -47,18 +43,15 @@ angular.service('notify', function(win) {
}; };
}, {$inject: ['$window']}); }, {$inject: ['$window']});
function myController(notifyService) { function myController(notifyService) {
this.callNotify = function(msg) { this.callNotify = function(msg) {
notifyService(msg); notifyService(msg);
}; };
} }
myController.$inject = ['notify']; myController.$inject = ['notify'];
</script> </script>
<div ng:controller="myController"> <div ng:controller="myController">
<p>Let's try this simple notify service, injected into the controller...</p> <p>Let's try this simple notify service, injected into the controller...</p>
<input ng:init="message='test'" type="text" name="message" /> <input ng:init="message='test'" type="text" name="message" />
@ -73,19 +66,14 @@ it('should test service', function(){
</doc:example> </doc:example>
## Related Topics ## Related Topics
{@link dev_guide.services.understanding_services Understanding Angular Services} {@link dev_guide.services.understanding_services Understanding Angular Services}
{@link dev_guide.services.creating_services Creating Angular Services} {@link dev_guide.services.creating_services Creating Angular Services}
{@link dev_guide.services.registering_services Registering Angular Services} {@link dev_guide.services.registering_services Registering Angular Services}
{@link dev_guide.services.managing_dependencies Managing Service Dependencies} {@link dev_guide.services.managing_dependencies Managing Service Dependencies}
{@link dev_guide.services.testing_services Testing Angular Services} {@link dev_guide.services.testing_services Testing Angular Services}
## Related API ## Related API
{@link api/angular.service Angular Service API} {@link api/angular.service Angular Service API}

View file

@ -3,20 +3,16 @@
@name Developer Guide: Angular Services: Managing Service Dependencies @name Developer Guide: Angular Services: Managing Service Dependencies
@description @description
Angular allows services to declare other services as dependencies needed for construction of their Angular allows services to declare other services as dependencies needed for construction of their
instances. instances.
To declare dependencies, you specify them in the factory function signature and via the `$inject` To declare dependencies, you specify them in the factory function signature and via the `$inject`
property, as an array of string identifiers. Optionally the `$inject` property declaration can be property, as an array of string identifiers. Optionally the `$inject` property declaration can be
dropped (see "Inferring `$inject`" but note that that is currently an experimental feature). dropped (see "Inferring `$inject`" but note that that is currently an experimental feature).
Here is an example of two services that depend on each other, as well as on other services that are Here is an example of two services that depend on each other, as well as on other services that are
provided by angular's web framework: provided by angular's web framework:
<pre> <pre>
/** /**
* batchLog service allows for messages to be queued in memory and flushed * batchLog service allows for messages to be queued in memory and flushed
@ -27,7 +23,6 @@ provided by angular's web framework:
angular.service('batchLog', function($defer, $log) { angular.service('batchLog', function($defer, $log) {
var messageQueue = []; var messageQueue = [];
function log() { function log() {
if (messageQueue.length) { if (messageQueue.length) {
$log('batchLog messages: ', messageQueue); $log('batchLog messages: ', messageQueue);
@ -36,14 +31,12 @@ function log() {
$defer(log, 50000); $defer(log, 50000);
} }
return function(message) { return function(message) {
messageQueue.push(message); messageQueue.push(message);
} }
}, {$inject: ['$defer', '$log']); }, {$inject: ['$defer', '$log']);
// note how we declared dependency on built-in $defer and $log services above // note how we declared dependency on built-in $defer and $log services above
/** /**
* routeTemplateMonitor monitors each $route change and logs the current * routeTemplateMonitor monitors each $route change and logs the current
* template via the batchLog service. * template via the batchLog service.
@ -55,10 +48,8 @@ $route.onChange(function() {
}, {$inject: ['$route', 'batchLog'], $eager: true}); }, {$inject: ['$route', 'batchLog'], $eager: true});
</pre> </pre>
Things to notice in this example: Things to notice in this example:
* The `batchLog` service depends on the built-in {@link api/angular.service.$defer $defer} and * The `batchLog` service depends on the built-in {@link api/angular.service.$defer $defer} and
{@link api/angular.service.$log $log} services, and allows messages to be logged into the {@link api/angular.service.$log $log} services, and allows messages to be logged into the
`console.log` in batches. `console.log` in batches.
@ -77,20 +68,15 @@ this array with IDs and their order that the injector uses to determine which se
order to inject. order to inject.
## Related Topics ## Related Topics
* {@link dev_guide.services.understanding_services Understanding Angular Services} * {@link dev_guide.services.understanding_services Understanding Angular Services}
* {@link dev_guide.services.creating_services Creating Angular Services} * {@link dev_guide.services.creating_services Creating Angular Services}
* {@link dev_guide.services.registering_services Registering Services} * {@link dev_guide.services.registering_services Registering Services}
* {@link dev_guide.services.injecting_controllers Injecting Services Into Controllers } * {@link dev_guide.services.injecting_controllers Injecting Services Into Controllers }
* {@link dev_guide.services.testing_services Testing Angular Services} * {@link dev_guide.services.testing_services Testing Angular Services}
## Related API ## Related API
* {@link api/angular.service Angular Service API} * {@link api/angular.service Angular Service API}
* {@link api/angular.injector Angular Injector API} * {@link api/angular.injector Angular Injector API}

View file

@ -3,18 +3,14 @@
@name Developer Guide: Angular Services @name Developer Guide: Angular Services
@description @description
Services are a feature that angular brings to client-side web apps from the server side, where Services are a feature that angular brings to client-side web apps from the server side, where
services have been commonly used for a long time. Services in angular apps are substitutable services have been commonly used for a long time. Services in angular apps are substitutable
objects that are wired together using {@link dev_guide.di dependency injection (DI)}. Services are objects that are wired together using {@link dev_guide.di dependency injection (DI)}. Services are
most often used with {@link dev_guide.di dependency injection}, also a key feature of angular apps. most often used with {@link dev_guide.di dependency injection}, also a key feature of angular apps.
## Related Topics ## Related Topics
* {@link dev_guide.services.understanding_services Understanding Angular Services} * {@link dev_guide.services.understanding_services Understanding Angular Services}
* {@link dev_guide.services.creating_services Creating Angular Services} * {@link dev_guide.services.creating_services Creating Angular Services}
* {@link dev_guide.services.registering_services Registering Angular Services} * {@link dev_guide.services.registering_services Registering Angular Services}
@ -22,8 +18,6 @@ most often used with {@link dev_guide.di dependency injection}, also a key featu
* {@link dev_guide.services.injecting_controllers Injecting Services Into Conrollers} * {@link dev_guide.services.injecting_controllers Injecting Services Into Conrollers}
* {@link dev_guide.services.testing_services Testing Angular Services} * {@link dev_guide.services.testing_services Testing Angular Services}
## Related API ## Related API
* {@link api/angular.service Angular Service API} * {@link api/angular.service Angular Service API}

View file

@ -3,12 +3,10 @@
@name Developer Guide: Angular Services: Registering Angular Services @name Developer Guide: Angular Services: Registering Angular Services
@description @description
To register a service, register a factory function that creates the service with angular's To register a service, register a factory function that creates the service with angular's
Injector. The Injector is exposed as {@link api/angular.scope.$service scope.$service}. The Injector. The Injector is exposed as {@link api/angular.scope.$service scope.$service}. The
following pseudo-code shows a simple service registration: following pseudo-code shows a simple service registration:
<pre> <pre>
angular.service('service id', function() { angular.service('service id', function() {
var shinyNewServiceInstance; var shinyNewServiceInstance;
@ -17,27 +15,21 @@ angular.service('service id', function() {
}); });
</pre> </pre>
Note that you are not registering a service instance, but rather a factory function that will Note that you are not registering a service instance, but rather a factory function that will
create this instance when called. create this instance when called.
# Instantiating Angular Services # Instantiating Angular Services
A service can be instantiated eagerly or lazily. By default angular instantiates services lazily, A service can be instantiated eagerly or lazily. By default angular instantiates services lazily,
which means that a service will be created only when it is needed for instantiation of a service or which means that a service will be created only when it is needed for instantiation of a service or
an application component that depends on it. In other words, angular won't instantiate lazy an application component that depends on it. In other words, angular won't instantiate lazy
services unless they are requested directly or indirectly by the application. services unless they are requested directly or indirectly by the application.
Eager services on the other hand, are instantiated right after the injector itself is created, Eager services on the other hand, are instantiated right after the injector itself is created,
which happens when the angular {@link dev_guide.bootstrap application initializes}. which happens when the angular {@link dev_guide.bootstrap application initializes}.
To override the default, you can request that a service is eagerly instantiated as follows: To override the default, you can request that a service is eagerly instantiated as follows:
<pre> <pre>
angular.service('service id', function() { angular.service('service id', function() {
var shinyNewServiceInstance; var shinyNewServiceInstance;
@ -46,12 +38,10 @@ angular.service('service id', function() {
}, {$eager: true}); }, {$eager: true});
</pre> </pre>
While it is tempting to declare services as eager, only in few cases it is actually useful. If you While it is tempting to declare services as eager, only in few cases it is actually useful. If you
are unsure whether to make a service eager, it likely doesn't need to be. To be more specific, a are unsure whether to make a service eager, it likely doesn't need to be. To be more specific, a
service should be declared as eager only if it fits one of these scenarios: service should be declared as eager only if it fits one of these scenarios:
* Nothing in your application declares this service as its dependency, and this service affects the * Nothing in your application declares this service as its dependency, and this service affects the
state or configuration of the application (e.g. a service that configures `$route` or `$resource` state or configuration of the application (e.g. a service that configures `$route` or `$resource`
services) services)
@ -60,7 +50,6 @@ because the service passively observes the application and it is optional for ot
components to depend on it. An example of this scenario is a service that monitors and logs components to depend on it. An example of this scenario is a service that monitors and logs
application memory usage. application memory usage.
Lastly, it is important to realize that all angular services are applicaiton singletons. This means Lastly, it is important to realize that all angular services are applicaiton singletons. This means
that there is only one instance of a given service per injector. Since angular is lethally allergic that there is only one instance of a given service per injector. Since angular is lethally allergic
to the global state, it is possible to create multiple injectors, each with its own instance of a to the global state, it is possible to create multiple injectors, each with its own instance of a
@ -68,19 +57,14 @@ given service, but that is rarely needed, except in tests where this property is
important. important.
## Related Topics ## Related Topics
* {@link dev_guide.services.understanding_services Understanding Angular Services} * {@link dev_guide.services.understanding_services Understanding Angular Services}
* {@link dev_guide.services.creating_services Creating Angular Services} * {@link dev_guide.services.creating_services Creating Angular Services}
* {@link dev_guide.services.managing_dependencies Managing Service Dependencies} * {@link dev_guide.services.managing_dependencies Managing Service Dependencies}
* {@link dev_guide.services.injecting_controllers Injecting Services Into Controllers } * {@link dev_guide.services.injecting_controllers Injecting Services Into Controllers }
* {@link dev_guide.services.testing_services Testing Angular Services} * {@link dev_guide.services.testing_services Testing Angular Services}
## Related API ## Related API
* {@link api/angular.service Angular Service API} * {@link api/angular.service Angular Service API}

View file

@ -3,29 +3,24 @@
@name Developer Guide: Angular Services: Testing Angular Services @name Developer Guide: Angular Services: Testing Angular Services
@description @description
Following is a unit test for the service in the example in {@link Following is a unit test for the service in the example in {@link
dev_guide.services.registering_services Registering Angular Services}. The unit test example uses dev_guide.services.registering_services Registering Angular Services}. The unit test example uses
Jasmine spy (mock) instead of a real browser alert. Jasmine spy (mock) instead of a real browser alert.
<pre> <pre>
var mock, notify; var mock, notify;
beforeEach(function() { beforeEach(function() {
mock = {alert: jasmine.createSpy()}; mock = {alert: jasmine.createSpy()};
notify = angular.service('notify')(mock); notify = angular.service('notify')(mock);
}); });
it('should not alert first two notifications', function() { it('should not alert first two notifications', function() {
notify('one'); notify('one');
notify('two'); notify('two');
expect(mock.alert).not.toHaveBeenCalled(); expect(mock.alert).not.toHaveBeenCalled();
}); });
it('should alert all after third notification', function() { it('should alert all after third notification', function() {
notify('one'); notify('one');
notify('two'); notify('two');
@ -33,7 +28,6 @@ notify('three');
expect(mock.alert).toHaveBeenCalledWith("one\ntwo\nthree"); expect(mock.alert).toHaveBeenCalledWith("one\ntwo\nthree");
}); });
it('should clear messages after alert', function() { it('should clear messages after alert', function() {
notify('one'); notify('one');
notify('two'); notify('two');
@ -47,24 +41,16 @@ expect(mock.alert.mostRecentCall.args).toEqual(["more\ntwo\nthird"]);
</pre> </pre>
## Related Topics ## Related Topics
* {@link dev_guide.services.understanding_services Understanding Angular Services} * {@link dev_guide.services.understanding_services Understanding Angular Services}
* {@link dev_guide.services.creating_services Creating Angular Services} * {@link dev_guide.services.creating_services Creating Angular Services}
* {@link dev_guide.services.registering_services Registering Angular Services} * {@link dev_guide.services.registering_services Registering Angular Services}
* {@link dev_guide.services.managing_dependencies Managing Service Dependencies} * {@link dev_guide.services.managing_dependencies Managing Service Dependencies}
* {@link dev_guide.services.injecting_controllers Injecting Services Into Conrollers} * {@link dev_guide.services.injecting_controllers Injecting Services Into Conrollers}
## Related API ## Related API
* {@link api/angular.service Angular Service API} * {@link api/angular.service Angular Service API}

View file

@ -3,46 +3,36 @@
@name Developer Guide: Angular Services: Understanding Angular Services @name Developer Guide: Angular Services: Understanding Angular Services
@description @description
Angular services are singletons that carry out specific tasks common to web apps, such as the Angular services are singletons that carry out specific tasks common to web apps, such as the
{@link api/angular.service.$xhr $xhr service} that provides low level access to the browser's {@link api/angular.service.$xhr $xhr service} that provides low level access to the browser's
`XMLHttpRequest` object. `XMLHttpRequest` object.
To use an angular service, you identify it as a dependency for the dependent (a controller, or To use an angular service, you identify it as a dependency for the dependent (a controller, or
another service) that depends on the service. Angular's dependency injection subsystem takes care another service) that depends on the service. Angular's dependency injection subsystem takes care
of the rest. The angular injector subsystem is in charge of service instantiation, resolution of of the rest. The angular injector subsystem is in charge of service instantiation, resolution of
dependencies, and provision of dependencies to factory functions as requested. dependencies, and provision of dependencies to factory functions as requested.
Angular injects dependencies using "constructor" injection (the service is passed in via a factory Angular injects dependencies using "constructor" injection (the service is passed in via a factory
function). Because JavaScript is a dynamically typed language, angular's dependency injection function). Because JavaScript is a dynamically typed language, angular's dependency injection
subsystem cannot use static types to identify service dependencies. For this reason a dependent subsystem cannot use static types to identify service dependencies. For this reason a dependent
must explicitly define its dependencies by using the `$inject` property. For example: must explicitly define its dependencies by using the `$inject` property. For example:
myController.$inject = ['$location']; myController.$inject = ['$location'];
The angular web framework provides a set of services for common operations. Like other core angular The angular web framework provides a set of services for common operations. Like other core angular
variables and identifiers, the built-in services always start with `$` (such as `$xhr` mentioned variables and identifiers, the built-in services always start with `$` (such as `$xhr` mentioned
above). You can also create your own custom services. above). You can also create your own custom services.
## Related Topics ## Related Topics
* {@link dev_guide.di About Angular Dependency Injection} * {@link dev_guide.di About Angular Dependency Injection}
* {@link dev_guide.services.creating_services Creating Angular Services} * {@link dev_guide.services.creating_services Creating Angular Services}
* {@link dev_guide.services.registering_services Registering Angular Services} * {@link dev_guide.services.registering_services Registering Angular Services}
* {@link dev_guide.services.managing_dependencies Managing Service Dependencies} * {@link dev_guide.services.managing_dependencies Managing Service Dependencies}
* {@link dev_guide.services.testing_services Testing Angular Services} * {@link dev_guide.services.testing_services Testing Angular Services}
## Related API ## Related API
* {@link api/angular.service Angular Service API} * {@link api/angular.service Angular Service API}
* {@link api/angular.injector Injector API} * {@link api/angular.injector Injector API}

View file

@ -4,66 +4,48 @@
@description @description
Angular includes built-in CSS classes, which in turn have predefined CSS styles. Angular includes built-in CSS classes, which in turn have predefined CSS styles.
# Built-in CSS classes # Built-in CSS classes
* `ng-exception` * `ng-exception`
**Usage:** angular applies this class to a DOM element if that element contains an Expression that **Usage:** angular applies this class to a DOM element if that element contains an Expression that
threw an exception when evaluated. threw an exception when evaluated.
**Styling:** The built-in styling of the ng-exception class displays an error message surrounded **Styling:** The built-in styling of the ng-exception class displays an error message surrounded
by a solid red border, for example: by a solid red border, for example:
<div class="ng-exception">Error message</div> <div class="ng-exception">Error message</div>
You can try to evaluate malformed expressions in {@link dev_guide.expressions expressions} to see You can try to evaluate malformed expressions in {@link dev_guide.expressions expressions} to see
the `ng-exception` class' styling. the `ng-exception` class' styling.
* `ng-validation-error` * `ng-validation-error`
**Usage:** angular applies this class to an input widget element if that element's input does not **Usage:** angular applies this class to an input widget element if that element's input does not
pass validation. Note that you set the validation criteria on the input widget element using the pass validation. Note that you set the validation criteria on the input widget element using the
Ng:validate or Ng:required directives. Ng:validate or Ng:required directives.
**Styling:** The built-in styling of the ng-validation-error class turns the border of the input **Styling:** The built-in styling of the ng-validation-error class turns the border of the input
box red and includes a hovering UI element that includes more details of the validation error. You box red and includes a hovering UI element that includes more details of the validation error. You
can see an example in {@link api/angular.widget.@ng:validate ng:validate example}. can see an example in {@link api/angular.widget.@ng:validate ng:validate example}.
## Overriding Styles for Angular CSS Classes ## Overriding Styles for Angular CSS Classes
To override the styles for angular's built-in CSS classes, you can do any of the following: To override the styles for angular's built-in CSS classes, you can do any of the following:
* Download the source code, edit angular.css, and host the source on your own server. * Download the source code, edit angular.css, and host the source on your own server.
* Create a local CSS file, overriding any styles that you'd like, and link to it from your HTML file * Create a local CSS file, overriding any styles that you'd like, and link to it from your HTML file
as you normally would: as you normally would:
<pre> <pre>
<link href="yourfile.css" rel="stylesheet" type="text/css"> <link href="yourfile.css" rel="stylesheet" type="text/css">
</pre> </pre>
## Related Topics ## Related Topics
* {@link dev_guide.templates Angular Templates} * {@link dev_guide.templates Angular Templates}
* {@link dev_guide.templates.css Working With CSS in Angular} * {@link dev_guide.templates.css Working With CSS in Angular}
* {@link dev_guide.templates.formatters Angular Formatters} * {@link dev_guide.templates.formatters Angular Formatters}

View file

@ -3,16 +3,13 @@
@name Developer Guide: Templates: Data Binding in Angular @name Developer Guide: Templates: Data Binding in Angular
@description @description
Data-binding in angular web apps is the automatic syncing of data between the model and view Data-binding in angular web apps is the automatic syncing of data between the model and view
components. The way that angular implements data-binding lets you treat the model as the components. The way that angular implements data-binding lets you treat the model as the
single-source-of-truth in your application. The view is a projection of the model at all times. single-source-of-truth in your application. The view is a projection of the model at all times.
When the model changes, the view reflects the change, and vice versa. When the model changes, the view reflects the change, and vice versa.
## Data Binding in Classical Template Systems ## Data Binding in Classical Template Systems
<img class="right" src="img/One_Way_Data_Binding.png"/> <img class="right" src="img/One_Way_Data_Binding.png"/>
Most templating systems bind data in only one direction: they merge template and model components Most templating systems bind data in only one direction: they merge template and model components
together into a view, as illustrated in the diagram. After the merge occurs, changes to the model together into a view, as illustrated in the diagram. After the merge occurs, changes to the model
@ -20,10 +17,8 @@ or related sections of the view are NOT automatically reflected in the view. Wor
that the user makes to the view are not reflected in the model. This means that the developer has that the user makes to the view are not reflected in the model. This means that the developer has
to write code that constantly syncs the view with the model and the model with the view. to write code that constantly syncs the view with the model and the model with the view.
## Data Binding in Angular Templates ## Data Binding in Angular Templates
<img class="right" src="img/Two_Way_Data_Binding.png"/> <img class="right" src="img/Two_Way_Data_Binding.png"/>
The way angular templates works is different, as illustrated in the diagram. They are different The way angular templates works is different, as illustrated in the diagram. They are different
because first the template (which is the uncompiled HTML along with any additional markup or because first the template (which is the uncompiled HTML along with any additional markup or
@ -33,16 +28,12 @@ the model are propagated to the view. This makes the model always the single-sou
the application state, greatly simplifying the programing model for the developer. You can think of the application state, greatly simplifying the programing model for the developer. You can think of
the view as simply an instant projection of your model. the view as simply an instant projection of your model.
Because the view is just a projection of the model, the controller is completely separated from the Because the view is just a projection of the model, the controller is completely separated from the
view and unaware of it. This makes testing a snap because it is easy to test your controller in view and unaware of it. This makes testing a snap because it is easy to test your controller in
isolation without the view and the related DOM/browser dependency. isolation without the view and the related DOM/browser dependency.
## Related Topics ## Related Topics
* {@link dev_guide.scopes Angular Scopes} * {@link dev_guide.scopes Angular Scopes}
* {@link dev_guide.templates Angular Templates} * {@link dev_guide.templates Angular Templates}

View file

@ -3,25 +3,20 @@
@name Developer Guide: Templates: Filters: Creating Angular Filters @name Developer Guide: Templates: Filters: Creating Angular Filters
@description @description
Writing your own filter is very easy: just define a JavaScript function on the `angular.filter` Writing your own filter is very easy: just define a JavaScript function on the `angular.filter`
object. object.
The framework passes in the input value as the first argument to your function. Any filter The framework passes in the input value as the first argument to your function. Any filter
arguments are passed in as additional function arguments. arguments are passed in as additional function arguments.
You can use these variables in the function: You can use these variables in the function:
* `this` — The current scope. * `this` — The current scope.
* `this.$element` — The DOM element containing the binding. The `$element` variable allows the * `this.$element` — The DOM element containing the binding. The `$element` variable allows the
filter to manipulate the DOM. filter to manipulate the DOM.
The following sample filter reverses a text string. In addition, it conditionally makes the The following sample filter reverses a text string. In addition, it conditionally makes the
text upper-case and assigns color. text upper-case and assigns color.
<doc:example> <doc:example>
<doc:source> <doc:source>
<script type="text/javascript"> <script type="text/javascript">
@ -42,7 +37,6 @@ text upper-case and assigns color.
}); });
</script> </script>
<input name="text" type="text" value="hello" /><br> <input name="text" type="text" value="hello" /><br>
No filter: {{text}}<br> No filter: {{text}}<br>
Reverse: {{text|reverse}}<br> Reverse: {{text|reverse}}<br>
@ -59,16 +53,11 @@ expect(binding('text|reverse')).toEqual('CBA');
</doc:example> </doc:example>
## Related Topics ## Related Topics
* {@link dev_guide.templates.filters Understanding Angular Filters} * {@link dev_guide.templates.filters Understanding Angular Filters}
* {@link dev_guide.compiler Angular HTML Compiler} * {@link dev_guide.compiler Angular HTML Compiler}
## Related API ## Related API
* {@link api/angular.filter Angular Filter API} * {@link api/angular.filter Angular Filter API}

View file

@ -3,36 +3,26 @@
@name Developer Guide: Templates: Understanding Angular Filters @name Developer Guide: Templates: Understanding Angular Filters
@description @description
Angular filters format data for display to the user. In addition to formatting data, filters can Angular filters format data for display to the user. In addition to formatting data, filters can
also modify the DOM. This allows filters to handle tasks such as conditionally applying CSS styles also modify the DOM. This allows filters to handle tasks such as conditionally applying CSS styles
to filtered output. to filtered output.
For example, you might have a data object that needs to be formatted according to the locale before For example, you might have a data object that needs to be formatted according to the locale before
displaying it to the user. You can pass expressions through a chain of filters like this: displaying it to the user. You can pass expressions through a chain of filters like this:
name | uppercase name | uppercase
The expression evaluator simply passes the value of name to `angular.filter.uppercase()`. The expression evaluator simply passes the value of name to `angular.filter.uppercase()`.
In addition to formatting data, filters can also modify the DOM. This allows filters to handle In addition to formatting data, filters can also modify the DOM. This allows filters to handle
tasks such as conditionally applying CSS styles to filtered output. tasks such as conditionally applying CSS styles to filtered output.
## Related Topics ## Related Topics
* {@link dev_guide.templates.filters.using_filters Using Angular Filters} * {@link dev_guide.templates.filters.using_filters Using Angular Filters}
* {@link dev_guide.templates.filters.creating_filters Creating Angular Filters} * {@link dev_guide.templates.filters.creating_filters Creating Angular Filters}
## Related API ## Related API
* {@link api/angular.filter Angular Filter API} * {@link api/angular.filter Angular Filter API}

View file

@ -3,35 +3,26 @@
@name Developer Guide: Templates: Filters: Using Angular Filters @name Developer Guide: Templates: Filters: Using Angular Filters
@description @description
Filters can be part of any {@link api/angular.scope} evaluation but are typically used to format Filters can be part of any {@link api/angular.scope} evaluation but are typically used to format
expressions in bindings in your templates: expressions in bindings in your templates:
{{ expression | filter }} {{ expression | filter }}
Filters typically transform the data to a new data type, formatting the data in the process. Filters typically transform the data to a new data type, formatting the data in the process.
Filters can also be chained, and can take optional arguments. Filters can also be chained, and can take optional arguments.
You can chain filters using this syntax: You can chain filters using this syntax:
{{ expression | filter1 | filter2 }} {{ expression | filter1 | filter2 }}
You can also pass colon-delimited arguments to filters, for example, to display the number 123 with You can also pass colon-delimited arguments to filters, for example, to display the number 123 with
2 decimal points: 2 decimal points:
123 | number:2 123 | number:2
Here are some examples that show values before and after applying different filters to an Here are some examples that show values before and after applying different filters to an
expression in a binding: expression in a binding:
* No filter: `{{1234.5678}}` => `1234.5678` * No filter: `{{1234.5678}}` => `1234.5678`
* Number filter: `{{1234.5678|number}}` => `1,234.57`. Notice the "," and rounding to two * Number filter: `{{1234.5678|number}}` => `1,234.57`. Notice the "," and rounding to two
significant digits. significant digits.
@ -40,16 +31,11 @@ arguments, separated by colons in a binding. For example, the "number" filter ta
argument that specifies how many digits to display to the right of the decimal point. argument that specifies how many digits to display to the right of the decimal point.
## Related Topics ## Related Topics
* {@link dev_guide.templates.filters Understanding Angular Filters} * {@link dev_guide.templates.filters Understanding Angular Filters}
* {@link dev_guide.templates.filters.creating_filters Creating Angular Filters} * {@link dev_guide.templates.filters.creating_filters Creating Angular Filters}
## Related API ## Related API
* {@link api/angular.filter Angular Filter API} * {@link api/angular.filter Angular Filter API}

View file

@ -3,19 +3,16 @@
@name Developer Guide: Templates: Angular Formatters: Creating Angular Formatters @name Developer Guide: Templates: Angular Formatters: Creating Angular Formatters
@description @description
To create your own formatter, you can simply register a pair of JavaScript functions with To create your own formatter, you can simply register a pair of JavaScript functions with
`angular.formatter`. One of your functions is used to parse text from the input widget into the `angular.formatter`. One of your functions is used to parse text from the input widget into the
data storage format; the other function is used to format stored data into user-readable text. data storage format; the other function is used to format stored data into user-readable text.
The following example demonstrates a "reverse" formatter. Data is stored in uppercase and in The following example demonstrates a "reverse" formatter. Data is stored in uppercase and in
reverse, but it is displayed in lower case and non-reversed. When a user edits the data model via reverse, but it is displayed in lower case and non-reversed. When a user edits the data model via
the input widget, the input is automatically parsed into the internal data storage format, and when the input widget, the input is automatically parsed into the internal data storage format, and when
the data changes in the model, it is automatically formatted to the user-readable form for display the data changes in the model, it is automatically formatted to the user-readable form for display
in the view. in the view.
<pre> <pre>
function reverse(text) { function reverse(text) {
var reversed = []; var reversed = [];
@ -25,7 +22,6 @@ reversed.unshift(text.charAt(i));
return reversed.join(''); return reversed.join('');
} }
angular.formatter('reverse', { angular.formatter('reverse', {
parse: function(value){ parse: function(value){
return reverse(value||'').toUpperCase(); return reverse(value||'').toUpperCase();
@ -36,7 +32,6 @@ return reverse(value||'').toLowerCase();
}); });
</pre> </pre>
<doc:example> <doc:example>
<doc:source> <doc:source>
<script type="text/javascript"> <script type="text/javascript">
@ -48,7 +43,6 @@ for (var i = 0; i < text.length; i++) {
return reversed.join(''); return reversed.join('');
} }
angular.formatter('reverse', { angular.formatter('reverse', {
parse: function(value){ parse: function(value){
return reverse(value||'').toUpperCase(); return reverse(value||'').toUpperCase();
@ -59,5 +53,3 @@ format: function(value){
}); });
</script> </script>

View file

@ -3,24 +3,18 @@
@name Developer Guide: Templates: Angular Formatters @name Developer Guide: Templates: Angular Formatters
@description @description
In angular, formatters are responsible for translating user-readable text entered in an {@link In angular, formatters are responsible for translating user-readable text entered in an {@link
api/angular.widget.HTML input widget} to a JavaScript object in the data model that the application api/angular.widget.HTML input widget} to a JavaScript object in the data model that the application
can manipulate. can manipulate.
You can use formatters in a template, and also in JavaScript. Angular provides built-in You can use formatters in a template, and also in JavaScript. Angular provides built-in
formatters, and of course you can create your own formatters. formatters, and of course you can create your own formatters.
## Related Topics ## Related Topics
* {@link dev_guide.templates.formatters.using_formatters Using Angular Formatters} * {@link dev_guide.templates.formatters.using_formatters Using Angular Formatters}
* {@link dev_guide.templates.formatters.creating_formatters Creating Angular Formatters} * {@link dev_guide.templates.formatters.creating_formatters Creating Angular Formatters}
## Related API ## Related API
* {@link api/angular.formatter Angular Formatter API} * {@link api/angular.formatter Angular Formatter API}

View file

@ -3,10 +3,7 @@
@name Developer Guide: Templates: Angular Formatters: Using Angular Formatters @name Developer Guide: Templates: Angular Formatters: Using Angular Formatters
@description @description
The following snippet shows how to use a formatter in a template. The formatter below is The following snippet shows how to use a formatter in a template. The formatter below is
`ng:format="reverse"`, added as an attribute to an `<input>` tag. `ng:format="reverse"`, added as an attribute to an `<input>` tag.
<pre> <pre>

View file

@ -3,17 +3,14 @@
@name Developer Guide: Understanding Angular Templates @name Developer Guide: Understanding Angular Templates
@description @description
An angular template is the declarative specification that, along with information from the model An angular template is the declarative specification that, along with information from the model
and controller, becomes the rendered view that a user sees in the browser. It is the static DOM, and controller, becomes the rendered view that a user sees in the browser. It is the static DOM,
containing HTML, CSS, and angular-specific elements and angular-specific element attributes. The containing HTML, CSS, and angular-specific elements and angular-specific element attributes. The
angular elements and attributes direct angular to add behavior and transform the template DOM into angular elements and attributes direct angular to add behavior and transform the template DOM into
the dynamic view DOM. the dynamic view DOM.
These are the types of angular elements and element attributes you can use in a template: These are the types of angular elements and element attributes you can use in a template:
* {@link dev_guide.compiler.directives Directive} — An attribute that augments an existing DOM * {@link dev_guide.compiler.directives Directive} — An attribute that augments an existing DOM
element. element.
* {@link dev_guide.compiler.widgets Widget} — A custom DOM element. An example of a built-in widget * {@link dev_guide.compiler.widgets Widget} — A custom DOM element. An example of a built-in widget
@ -25,16 +22,13 @@ curly brace notation `{{ }}` to bind expressions to elements is built-in angular
* {@link dev_guide.templates.formatters Formatter} — Lets you format the input object into a user * {@link dev_guide.templates.formatters Formatter} — Lets you format the input object into a user
readable view. readable view.
Note: In addition to declaring the elements above in templates, you can also access these elements Note: In addition to declaring the elements above in templates, you can also access these elements
in JavaScript code. in JavaScript code.
The following code snippet shows a simple angular template made up of standard HTML tags along with The following code snippet shows a simple angular template made up of standard HTML tags along with
angular {@link dev_guide.compiler.directives directives}, {@link dev_guide.compiler.markup markup}, angular {@link dev_guide.compiler.directives directives}, {@link dev_guide.compiler.markup markup},
and {@link dev_guide.expressions expressions}: and {@link dev_guide.expressions expressions}:
<pre> <pre>
<html> <html>
<!-- Body tag augmented with ng:controller directive --> <!-- Body tag augmented with ng:controller directive -->
@ -49,7 +43,6 @@ and {@link dev_guide.expressions expressions}:
</html> </html>
</pre> </pre>
In a simple single-page app, the template consists of HTML, CSS, and angular directives contained In a simple single-page app, the template consists of HTML, CSS, and angular directives contained
in just one HTML file (usually `index.html`). In a more complex app, you can display multiple views in just one HTML file (usually `index.html`). In a more complex app, you can display multiple views
within one main page using "partials", which are segments of template located in separate HTML within one main page using "partials", which are segments of template located in separate HTML
@ -59,17 +52,12 @@ example of this technique is shown in the {@link tutorial/ angular tutorial}, in
eight. eight.
## Related Topics ## Related Topics
* {@link dev_guide.templates.filters Angular Filters} * {@link dev_guide.templates.filters Angular Filters}
* {@link dev_guide.templates.formatters Angular Formatters} * {@link dev_guide.templates.formatters Angular Formatters}
* {@link dev_guide.templates.validators Angular Validators} * {@link dev_guide.templates.validators Angular Validators}
## Related API ## Related API
* {@link api/index API Reference} * {@link api/index API Reference}

View file

@ -4,17 +4,13 @@
@description @description
To create a custom validator, you simply add your validator code as a method onto the To create a custom validator, you simply add your validator code as a method onto the
`angular.validator` object and provide input(s) for the validator function. Each input provided is `angular.validator` object and provide input(s) for the validator function. Each input provided is
treated as an argument to the validator function. Any additional inputs should be separated by treated as an argument to the validator function. Any additional inputs should be separated by
commas. commas.
The following bit of pseudo-code shows how to set up a custom validator: The following bit of pseudo-code shows how to set up a custom validator:
<pre> <pre>
angular.validator('your_validator', function(input [,additional params]) { angular.validator('your_validator', function(input [,additional params]) {
[your validation code]; [your validation code];
@ -26,22 +22,17 @@ angular.validator('your_validator', function(input [,additional params]) {
} }
</pre> </pre>
Note that this validator returns "true" when the user's input is incorrect, as in "Yes, it's true, Note that this validator returns "true" when the user's input is incorrect, as in "Yes, it's true,
there was a problem with that input". If you prefer to provide more information when a validator there was a problem with that input". If you prefer to provide more information when a validator
detects a problem with input, you can specify an error message in the validator that angular will detects a problem with input, you can specify an error message in the validator that angular will
display when the user hovers over the input widget. display when the user hovers over the input widget.
To specify an error message, replace "`return true;`" with an error string, for example: To specify an error message, replace "`return true;`" with an error string, for example:
return "Must be a value between 1 and 5!"; return "Must be a value between 1 and 5!";
Following is a sample UPS Tracking Number validator: Following is a sample UPS Tracking Number validator:
<doc:example> <doc:example>
<doc:source> <doc:source>
<script> <script>
@ -60,7 +51,6 @@ expect(element('input[name=trackNo]').attr('class')).
not().toMatch(/ng-validation-error/); not().toMatch(/ng-validation-error/);
}); });
it('should not validate in correct UPS tracking number', function() { it('should not validate in correct UPS tracking number', function() {
input('trackNo').enter('foo'); input('trackNo').enter('foo');
expect(element('input[name=trackNo]').attr('class')). expect(element('input[name=trackNo]').attr('class')).
@ -69,32 +59,24 @@ expect(element('input[name=trackNo]').attr('class')).
</doc:scenario> </doc:scenario>
</doc:example> </doc:example>
In this sample validator, we specify a regular expression against which to test the user's input. In this sample validator, we specify a regular expression against which to test the user's input.
Note that when the user's input matches `regexp`, the function returns "false" (""); otherwise it Note that when the user's input matches `regexp`, the function returns "false" (""); otherwise it
returns the specified error message ("true"). returns the specified error message ("true").
Note: you can also access the current angular scope and DOM element objects in your validator Note: you can also access the current angular scope and DOM element objects in your validator
functions as follows: functions as follows:
* `this` === The current angular scope. * `this` === The current angular scope.
* `this.$element` === The DOM element that contains the binding. This allows the filter to * `this.$element` === The DOM element that contains the binding. This allows the filter to
manipulate the DOM in addition to transforming the input. manipulate the DOM in addition to transforming the input.
## Related Topics ## Related Topics
* {@link dev_guide.templates Angular Templates} * {@link dev_guide.templates Angular Templates}
* {@link dev_guide.templates.filters Angular Filters} * {@link dev_guide.templates.filters Angular Filters}
* {@link dev_guide.templates.formatters Angular Formatters} * {@link dev_guide.templates.formatters Angular Formatters}
## Related API ## Related API
* {@link api/angular.validator API Validator Reference} * {@link api/angular.validator API Validator Reference}

View file

@ -3,11 +3,9 @@
@name Developer Guide: Templates: Understanding Angular Validators @name Developer Guide: Templates: Understanding Angular Validators
@description @description
Angular validators are attributes that test the validity of different types of user input. Angular Angular validators are attributes that test the validity of different types of user input. Angular
provides a set of built-in input validators: provides a set of built-in input validators:
* {@link api/angular.validator.phone phone number} * {@link api/angular.validator.phone phone number}
* {@link api/angular.validator.number number} * {@link api/angular.validator.number number}
* {@link api/angular.validator.integer integer} * {@link api/angular.validator.integer integer}
@ -18,35 +16,26 @@ provides a set of built-in input validators:
* {@link api/angular.validator.url URLs} * {@link api/angular.validator.url URLs}
* {@link api/angular.validator.asynchronous asynchronous} * {@link api/angular.validator.asynchronous asynchronous}
You can also create your own custom validators. You can also create your own custom validators.
# Using Angular Validators # Using Angular Validators
You can use angular validators in HTML template bindings, and in JavaScript: You can use angular validators in HTML template bindings, and in JavaScript:
* Validators in HTML Template Bindings * Validators in HTML Template Bindings
<pre> <pre>
<input ng:validator="validator_type:parameters" [...]> <input ng:validator="validator_type:parameters" [...]>
</pre> </pre>
* Validators in JavaScript * Validators in JavaScript
<pre> <pre>
angular.validator.[validator_type](parameters) angular.validator.[validator_type](parameters)
</pre> </pre>
The following example shows how to use the built-in angular integer validator: The following example shows how to use the built-in angular integer validator:
<doc:example> <doc:example>
<doc:source> <doc:source>
Change me: <input type="text" name="number" ng:validate="integer" value="123"> Change me: <input type="text" name="number" ng:validate="integer" value="123">
@ -64,19 +53,15 @@ The following example shows how to use the built-in angular integer validator:
</doc:scenario> </doc:scenario>
</doc:example> </doc:example>
# Creating an Angular Validator # Creating an Angular Validator
To create a custom validator, you simply add your validator code as a method onto the To create a custom validator, you simply add your validator code as a method onto the
`angular.validator` object and provide input(s) for the validator function. Each input provided is `angular.validator` object and provide input(s) for the validator function. Each input provided is
treated as an argument to the validator function. Any additional inputs should be separated by treated as an argument to the validator function. Any additional inputs should be separated by
commas. commas.
The following bit of pseudo-code shows how to set up a custom validator: The following bit of pseudo-code shows how to set up a custom validator:
<pre> <pre>
angular.validator('your_validator', function(input [,additional params]) { angular.validator('your_validator', function(input [,additional params]) {
[your validation code]; [your validation code];
@ -88,22 +73,17 @@ angular.validator('your_validator', function(input [,additional params]) {
} }
</pre> </pre>
Note that this validator returns "true" when the user's input is incorrect, as in "Yes, it's true, Note that this validator returns "true" when the user's input is incorrect, as in "Yes, it's true,
there was a problem with that input". If you prefer to provide more information when a validator there was a problem with that input". If you prefer to provide more information when a validator
detects a problem with input, you can specify an error message in the validator that angular will detects a problem with input, you can specify an error message in the validator that angular will
display when the user hovers over the input widget. display when the user hovers over the input widget.
To specify an error message, replace "`return true;`" with an error string, for example: To specify an error message, replace "`return true;`" with an error string, for example:
return "Must be a value between 1 and 5!"; return "Must be a value between 1 and 5!";
Following is a sample UPS Tracking Number validator: Following is a sample UPS Tracking Number validator:
<doc:example> <doc:example>
<doc:source> <doc:source>
<script> <script>
@ -122,7 +102,6 @@ it('should validate correct UPS tracking number', function() {
not().toMatch(/ng-validation-error/); not().toMatch(/ng-validation-error/);
}); });
it('should not validate in correct UPS tracking number', function() { it('should not validate in correct UPS tracking number', function() {
input('trackNo').enter('foo'); input('trackNo').enter('foo');
expect(element('input[name=trackNo]').attr('class')). expect(element('input[name=trackNo]').attr('class')).
@ -131,30 +110,22 @@ it('should not validate in correct UPS tracking number', function() {
</doc:scenario> </doc:scenario>
</doc:example> </doc:example>
In this sample validator, we specify a regular expression against which to test the user's input. In this sample validator, we specify a regular expression against which to test the user's input.
Note that when the user's input matches `regexp`, the function returns "false" (""); otherwise it Note that when the user's input matches `regexp`, the function returns "false" (""); otherwise it
returns the specified error message ("true"). returns the specified error message ("true").
Note: you can also access the current angular scope and DOM element objects in your validator Note: you can also access the current angular scope and DOM element objects in your validator
functions as follows: functions as follows:
* `this` === The current angular scope. * `this` === The current angular scope.
* `this.$element` === The DOM element that contains the binding. This allows the filter to * `this.$element` === The DOM element that contains the binding. This allows the filter to
manipulate the DOM in addition to transforming the input. manipulate the DOM in addition to transforming the input.
## Related Topics ## Related Topics
* {@link dev_guide.templates Angular Templates} * {@link dev_guide.templates Angular Templates}
## Related API ## Related API
* {@link api/angular.validator Validator API} * {@link api/angular.validator Validator API}

View file

@ -3,7 +3,6 @@
@name Developer Guide: Unit Testing @name Developer Guide: Unit Testing
@description @description
JavaScript is a dynamically typed language which comes with great power of expression, but it also JavaScript is a dynamically typed language which comes with great power of expression, but it also
come with almost no-help from the compiler. For this reason we feel very strongly that any code come with almost no-help from the compiler. For this reason we feel very strongly that any code
written in JavaScript needs to come with a strong set of tests. We have built many features into written in JavaScript needs to come with a strong set of tests. We have built many features into
@ -27,7 +26,6 @@ DOM in the right way. Angular is written with testability in mind, but it still
do the right thing. We tried to make the right thing easy, but angular is not magic, which means if do the right thing. We tried to make the right thing easy, but angular is not magic, which means if
you don't follow these, you may very well end up with an untestable application. you don't follow these, you may very well end up with an untestable application.
## Dependency Inject ## Dependency Inject
There are several ways in which you can get a hold of a dependency: There are several ways in which you can get a hold of a dependency:
1. You could create it using the `new` operator. 1. You could create it using the `new` operator.
@ -36,18 +34,14 @@ There are several ways in which you can get a hold of a dependency:
the registry? Must likely by looking it up in a well know place. See #2) the registry? Must likely by looking it up in a well know place. See #2)
4. You could expect that the it be handed to you. 4. You could expect that the it be handed to you.
Out of the list above only the last of is testable. Lets look at why: Out of the list above only the last of is testable. Lets look at why:
### Using the `new` operator ### Using the `new` operator
While there is nothing wrong with the `new` operator fundamentally the issue is that calling a new While there is nothing wrong with the `new` operator fundamentally the issue is that calling a new
on a constructor permanently binds the call site to the type. For example lets say that we are on a constructor permanently binds the call site to the type. For example lets say that we are
trying to instantiate an `XHR` so that we can get some data from the server. trying to instantiate an `XHR` so that we can get some data from the server.
<pre> <pre>
function MyClass(){ function MyClass(){
this.doWork = function(){ this.doWork = function(){
@ -59,13 +53,11 @@ function MyClass(){
} }
</pre> </pre>
The issue becomes, that in tests, we would very much like to instantiate a `MockXHR` which would The issue becomes, that in tests, we would very much like to instantiate a `MockXHR` which would
allow us to return fake data and simulate network failures. By calling `new XHR()` we are allow us to return fake data and simulate network failures. By calling `new XHR()` we are
permanently bound to the actual one, and there is no good way to replace it. Yes there is monkey permanently bound to the actual one, and there is no good way to replace it. Yes there is monkey
patching, that is a bad idea for many reasons, which is outside the scope of this document. patching, that is a bad idea for many reasons, which is outside the scope of this document.
The class above is hard to test since we have to resort to monkey patching: The class above is hard to test since we have to resort to monkey patching:
<pre> <pre>
var oldXHR = XHR; var oldXHR = XHR;
@ -77,12 +69,9 @@ XHR = oldXHR; // if you forget this bad things will happen
</pre> </pre>
### Global look-up: ### Global look-up:
Another way to approach the problem is look for the service in a well known location. Another way to approach the problem is look for the service in a well known location.
<pre> <pre>
function MyClass(){ function MyClass(){
this.doWork = function(){ this.doWork = function(){
@ -95,7 +84,6 @@ function MyClass(){
} }
</pre> </pre>
While no new instance of dependency is being created, it is fundamentally the same as `new`, in While no new instance of dependency is being created, it is fundamentally the same as `new`, in
that there is no good way to intercept the call to `global.xhr` for testing purposes, other then that there is no good way to intercept the call to `global.xhr` for testing purposes, other then
through monkey patching. The basic issue for testing is that global variable needs to be mutated in through monkey patching. The basic issue for testing is that global variable needs to be mutated in
@ -103,7 +91,6 @@ order to replace it with call to a mock method. For further explanation why this
http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/ Brittle Global http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/ Brittle Global
State & Singletons} State & Singletons}
The class above is hard to test since we have to change global state: The class above is hard to test since we have to change global state:
<pre> <pre>
var oldXHR = glabal.xhr; var oldXHR = glabal.xhr;
@ -115,15 +102,11 @@ global.xhr = oldXHR; // if you forget this bad things will happen
</pre> </pre>
### Service Registry: ### Service Registry:
It may seem as that this can be solved by having a registry for all of the services, and then It may seem as that this can be solved by having a registry for all of the services, and then
having the tests replace the services as needed. having the tests replace the services as needed.
<pre> <pre>
function MyClass() { function MyClass() {
var serviceRegistry = ????; var serviceRegistry = ????;
@ -137,13 +120,11 @@ function MyClass() {
} }
</pre> </pre>
However, where dose the serviceRegistry come from? if it is: However, where dose the serviceRegistry come from? if it is:
* `new`-ed up, the the test has no chance to reset the services for testing * `new`-ed up, the the test has no chance to reset the services for testing
* global look-up, then the service returned is global as well (but resetting is easier, since * global look-up, then the service returned is global as well (but resetting is easier, since
there is only one global variable to be reset). there is only one global variable to be reset).
The class above is hard to test since we have to change global state: The class above is hard to test since we have to change global state:
<pre> <pre>
var oldServiceLocator = glabal.serviceLocator; var oldServiceLocator = glabal.serviceLocator;
@ -155,12 +136,9 @@ glabal.serviceLocator = oldServiceLocator; // if you forget this bad things will
</pre> </pre>
### Passing in Dependencies: ### Passing in Dependencies:
Lastly the dependency can be passed in. Lastly the dependency can be passed in.
<pre> <pre>
function MyClass(xhr) { function MyClass(xhr) {
this.doWork = function(){ this.doWork = function(){
@ -227,11 +205,9 @@ function PasswordController(){
} }
</pre> </pre>
The code above is problematic from testability, since it requires your test to have the right kind The code above is problematic from testability, since it requires your test to have the right kind
of DOM present when the code executes. The test would look like this: of DOM present when the code executes. The test would look like this:
<pre> <pre>
var input = $('<input type="text"/>'); var input = $('<input type="text"/>');
var span = $('<span>'); var span = $('<span>');
@ -246,11 +222,9 @@ expect(span.text()).toEqual('weak');
$('body').html(''); $('body').html('');
</pre> </pre>
In angular the controllers are strictly separated from the DOM manipulation logic which results in In angular the controllers are strictly separated from the DOM manipulation logic which results in
a much easier testability story as can be seen in this example: a much easier testability story as can be seen in this example:
<pre> <pre>
function PasswordCntrl(){ function PasswordCntrl(){
this.password = ''; this.password = '';
@ -267,10 +241,8 @@ function PasswordCntrl(){
} }
</pre> </pre>
and the tests is straight forward and the tests is straight forward
<pre> <pre>
var pc = new PasswordController(); var pc = new PasswordController();
pc.password('abc'); pc.password('abc');
@ -278,37 +250,29 @@ pc.grade();
expect(span.strength).toEqual('weak'); expect(span.strength).toEqual('weak');
</pre> </pre>
Notice that the test is not only much shorter but it is easier to follow what is going on. We say Notice that the test is not only much shorter but it is easier to follow what is going on. We say
that such a test tells a story, rather then asserting random bits which don't seem to be related. that such a test tells a story, rather then asserting random bits which don't seem to be related.
## Filters ## Filters
{@link api/angular.filter Filters} are functions which transform the data into user readable {@link api/angular.filter Filters} are functions which transform the data into user readable
format. They are important because they remove the formatting responsibility from the application format. They are important because they remove the formatting responsibility from the application
logic, further simplifying the application logic. logic, further simplifying the application logic.
<pre> <pre>
angular.filter('length', function(text){ angular.filter('length', function(text){
return (''+(text||'')).length; return (''+(text||'')).length;
}); });
var length = angular.filter('length'); var length = angular.filter('length');
expect(length(null)).toEqual(0); expect(length(null)).toEqual(0);
expect(length('abc')).toEqual(3); expect(length('abc')).toEqual(3);
</pre> </pre>
## Directives ## Directives
Directives in angular are responsible for updating the DOM when the state of the model changes. Directives in angular are responsible for updating the DOM when the state of the model changes.
## Mocks ## Mocks
oue oue
## Global State Isolation ## Global State Isolation

View file

@ -3,78 +3,59 @@
@name Developer Guide @name Developer Guide
@description @description
Welcome to the angular Developer Guide. If you are here to learn the details of how to use angular Welcome to the angular Developer Guide. If you are here to learn the details of how to use angular
to develop web apps, you've come to the right place. to develop web apps, you've come to the right place.
If you are completely or relatively unfamiliar with angular, you may want to check out one or both If you are completely or relatively unfamiliar with angular, you may want to check out one or both
of the following documents before returning here to the Developer Guide: of the following documents before returning here to the Developer Guide:
* {@link misc/started Getting Started} * {@link misc/started Getting Started}
* {@link tutorial/index Angular Tutorial} * {@link tutorial/index Angular Tutorial}
<hr> <hr>
## {@link dev_guide.overview Overview of Angular} ## {@link dev_guide.overview Overview of Angular}
## {@link dev_guide.bootstrap Initializing Angular} ## {@link dev_guide.bootstrap Initializing Angular}
* {@link dev_guide.bootstrap.auto_bootstrap Understanding Automatic Initialization} * {@link dev_guide.bootstrap.auto_bootstrap Understanding Automatic Initialization}
* {@link dev_guide.bootstrap.manual_bootstrap Understanding Manual Initialization} * {@link dev_guide.bootstrap.manual_bootstrap Understanding Manual Initialization}
## {@link dev_guide.mvc About MVC in Angular} ## {@link dev_guide.mvc About MVC in Angular}
* {@link dev_guide.mvc.understanding_model Understanding the Model Component} * {@link dev_guide.mvc.understanding_model Understanding the Model Component}
* {@link dev_guide.mvc.understanding_controller Understanding the Controller Component} * {@link dev_guide.mvc.understanding_controller Understanding the Controller Component}
* {@link dev_guide.mvc.understanding_view Understanding the View Component} * {@link dev_guide.mvc.understanding_view Understanding the View Component}
## {@link dev_guide.scopes Angular Scope Objects} ## {@link dev_guide.scopes Angular Scope Objects}
* {@link dev_guide.scopes.understanding_scopes Understanding Angular Scope Objects} * {@link dev_guide.scopes.understanding_scopes Understanding Angular Scope Objects}
* {@link dev_guide.scopes.working_scopes Working With Angular Scopes} * {@link dev_guide.scopes.working_scopes Working With Angular Scopes}
* {@link dev_guide.scopes.controlling_scopes Applying Controllers to Scopes} * {@link dev_guide.scopes.controlling_scopes Applying Controllers to Scopes}
* {@link dev_guide.scopes.updating_scopes Updating Scope Properties} * {@link dev_guide.scopes.updating_scopes Updating Scope Properties}
## {@link dev_guide.compiler Angular HTML Compiler} ## {@link dev_guide.compiler Angular HTML Compiler}
* {@link dev_guide.compiler.directives Understanding Angular Directives} * {@link dev_guide.compiler.directives Understanding Angular Directives}
* {@link dev_guide.compiler.widgets Understanding Angular Widgets} * {@link dev_guide.compiler.widgets Understanding Angular Widgets}
* {@link dev_guide.compiler.directives_widgets Comparing Directives and Widgets} * {@link dev_guide.compiler.directives_widgets Comparing Directives and Widgets}
* {@link dev_guide.compiler.markup Understanding Angular Markup} * {@link dev_guide.compiler.markup Understanding Angular Markup}
## {@link dev_guide.templates Angular Templates} ## {@link dev_guide.templates Angular Templates}
* {@link dev_guide.templates.filters Understanding Angular Filters} * {@link dev_guide.templates.filters Understanding Angular Filters}
* {@link dev_guide.templates.formatters Understanding Angular Formatters} * {@link dev_guide.templates.formatters Understanding Angular Formatters}
* {@link dev_guide.templates.validators Understanding Angular Validators} * {@link dev_guide.templates.validators Understanding Angular Validators}
## {@link dev_guide.services Angular Services} ## {@link dev_guide.services Angular Services}
* {@link dev_guide.services.understanding_services Understanding Angular Services} * {@link dev_guide.services.understanding_services Understanding Angular Services}
* {@link dev_guide.services.creating_services Creating Angular Services} * {@link dev_guide.services.creating_services Creating Angular Services}
* {@link dev_guide.services.registering_services Registering Angular Services} * {@link dev_guide.services.registering_services Registering Angular Services}
* {@link dev_guide.services.managing_dependencies Managing Service Dependencies} * {@link dev_guide.services.managing_dependencies Managing Service Dependencies}
* {@link dev_guide.services.testing_services Testing Angular Services} * {@link dev_guide.services.testing_services Testing Angular Services}
## {@link dev_guide.di About Dependency Injection} ## {@link dev_guide.di About Dependency Injection}
* {@link dev_guide.di.understanding_di Understanding DI in Angular} * {@link dev_guide.di.understanding_di Understanding DI in Angular}
* {@link dev_guide.di.using_di_controllers Using DI in Controllers} * {@link dev_guide.di.using_di_controllers Using DI in Controllers}

View file

@ -3,8 +3,6 @@
@description @description
* <a href="#H1_1">License</a> * <a href="#H1_1">License</a>
* <a href="#H1_2">Contributing to Source Code</a> * <a href="#H1_2">Contributing to Source Code</a>
* <a href="#H1_3">Applying Code Standards</a> * <a href="#H1_3">Applying Code Standards</a>
@ -12,349 +10,243 @@
* <a href="#H1_5">Submitting Your Changes</a> * <a href="#H1_5">Submitting Your Changes</a>
<a name="H1_1"></a> <a name="H1_1"></a>
# License # License
`Angular` is an open source project licensed under the {@link `Angular` is an open source project licensed under the {@link
http://github.com/angular/angular.js/blob/master/LICENSE MIT license}. Your contributions are http://github.com/angular/angular.js/blob/master/LICENSE MIT license}. Your contributions are
always welcome. When working with `angular` source base, please follow the guidelines provided on always welcome. When working with `angular` source base, please follow the guidelines provided on
this page. this page.
<a name="H1_2"></a> <a name="H1_2"></a>
# Contributing to Source Code # Contributing to Source Code
We'd love for you to contribute to our source code and to make `angular` even better than it is We'd love for you to contribute to our source code and to make `angular` even better than it is
today! Here are the guidelines we'd like you to use: today! Here are the guidelines we'd like you to use:
* Major changes that you intend to contribute to the project must be discussed first on our {@link * Major changes that you intend to contribute to the project must be discussed first on our {@link
https://groups.google.com/forum/?hl=en#!forum/angular mailing list} so that we can better https://groups.google.com/forum/?hl=en#!forum/angular mailing list} so that we can better
coordinate our efforts, prevent duplication of work, and help you to craft the change so that it coordinate our efforts, prevent duplication of work, and help you to craft the change so that it
is successfully accepted upstream. is successfully accepted upstream.
* Small changes and bug fixes can be crafted and submitted to Github as a <a href="#H1_5">pull * Small changes and bug fixes can be crafted and submitted to Github as a <a href="#H1_5">pull
request</a>. request</a>.
<a name="H1_3"></a> <a name="H1_3"></a>
# Applying Code Standards # Applying Code Standards
To ensure consistency throughout the source code, keep these rules in mind as you are working: To ensure consistency throughout the source code, keep these rules in mind as you are working:
* All features or bug fixes must be tested by one or more <a href="#unit-tests">specs</a>. * All features or bug fixes must be tested by one or more <a href="#unit-tests">specs</a>.
* All public API methods must be documented with ngdoc, an extended version of jsdoc (we added * All public API methods must be documented with ngdoc, an extended version of jsdoc (we added
support for markdown and templating via `@ngdoc` tag). To see how we document our APIs, please support for markdown and templating via `@ngdoc` tag). To see how we document our APIs, please
check out the existing ngdocs. check out the existing ngdocs.
* With the exceptions listed below, we follow the rules contained in {@link * With the exceptions listed below, we follow the rules contained in {@link
http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml Google's JavaScript Style http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml Google's JavaScript Style
Guide}: Guide}:
* Do not use namespaces: Instead, we wrap the entire `angular` code base in an anonymous closure * Do not use namespaces: Instead, we wrap the entire `angular` code base in an anonymous closure
and export our API explicitly rather than implicitly. and export our API explicitly rather than implicitly.
* Wrap all code at 100 characters. * Wrap all code at 100 characters.
* Instead of complex inheritance hierarchies, we prefer simple objects. We use prototypical * Instead of complex inheritance hierarchies, we prefer simple objects. We use prototypical
inheritance only when absolutely necessary. inheritance only when absolutely necessary.
* We love functions and closures and, whenever possible, prefer them over objects. * We love functions and closures and, whenever possible, prefer them over objects.
* To write concise code that can be better minified, internally we use aliases that map to the * To write concise code that can be better minified, internally we use aliases that map to the
external API. See our existing code to see what we mean. external API. See our existing code to see what we mean.
* We don't go crazy with type annotations for private internal APIs unless it's an internal API * We don't go crazy with type annotations for private internal APIs unless it's an internal API
that is used throughout `angular`. The best guidance is to do what makes the most sense. that is used throughout `angular`. The best guidance is to do what makes the most sense.
<a name="H1_4"></a> <a name="H1_4"></a>
# Checking Out and Building Angular # Checking Out and Building Angular
The `angular` source code is hosted at {@link http://github.com Github}, which we also use to The `angular` source code is hosted at {@link http://github.com Github}, which we also use to
accept code contributions. Several steps are needed to check out and build `angular`: accept code contributions. Several steps are needed to check out and build `angular`:
## Installation Dependencies ## Installation Dependencies
Before you can build `angular`, you must install or configure the following dependencies on your Before you can build `angular`, you must install or configure the following dependencies on your
machine: machine:
* {@link http://rake.rubyforge.org Rake}: We use Rake as our build system, which is pre-installed * {@link http://rake.rubyforge.org Rake}: We use Rake as our build system, which is pre-installed
on most Macintosh and Linux machines. If that is not true in your case, you can grab it from the on most Macintosh and Linux machines. If that is not true in your case, you can grab it from the
Rake website. Rake website.
* {@link http://nodejs.org Node.js}: We use Node to generate the documentation and to run a * {@link http://nodejs.org Node.js}: We use Node to generate the documentation and to run a
development web server. Depending on your system, you can install Node either from source or as a development web server. Depending on your system, you can install Node either from source or as a
pre-packaged bundle. pre-packaged bundle.
* Java: The Java runtime is used to run {@link http://code.google.com/p/js-test-driver * Java: The Java runtime is used to run {@link http://code.google.com/p/js-test-driver
JsTestDriver} (JSTD), which we use to run our unit test suite. JSTD binaries are part of the JsTestDriver} (JSTD), which we use to run our unit test suite. JSTD binaries are part of the
`angular` source base, which means there is no need to install or configure it separately. `angular` source base, which means there is no need to install or configure it separately.
* Git: The {@link http://help.github.com/mac-git-installation Github Guide to Installing Git} is * Git: The {@link http://help.github.com/mac-git-installation Github Guide to Installing Git} is
quite a good source for information on Git. quite a good source for information on Git.
## Creating a Github Account and Forking Angular ## Creating a Github Account and Forking Angular
To create a Github account, follow the instructions {@link https://github.com/signup/free here}. To create a Github account, follow the instructions {@link https://github.com/signup/free here}.
Afterwards, go ahead and {@link http://help.github.com/forking fork} the {@link Afterwards, go ahead and {@link http://help.github.com/forking fork} the {@link
https://github.com/angular/angular.js main angular repository}. https://github.com/angular/angular.js main angular repository}.
## Building `Angular` ## Building `Angular`
To build `angular`, you check out the source code and use Rake to generate the non-minified and To build `angular`, you check out the source code and use Rake to generate the non-minified and
minified `angular` files: minified `angular` files:
1. To clone your Github repository, run: 1. To clone your Github repository, run:
git clone git@github.com:<github username>/angular.js.git git clone git@github.com:<github username>/angular.js.git
2. To go to the `angular` directory, run: 2. To go to the `angular` directory, run:
cd angular.js cd angular.js
3. To add the main `angular` repository as an upstream remote to your repository, run: 3. To add the main `angular` repository as an upstream remote to your repository, run:
git remote add upstream https://github.com/angular/angular.js.git git remote add upstream https://github.com/angular/angular.js.git
4. To build `angular`, run: 4. To build `angular`, run:
rake package rake package
The build output can be located under the `build` directory. It consists of the following files and The build output can be located under the `build` directory. It consists of the following files and
directories: directories:
* `angular-<version>.tgz` — This is the complete tarball, which contains all of the release build * `angular-<version>.tgz` — This is the complete tarball, which contains all of the release build
artifacts. artifacts.
* `angular.js` — The non-minified `angular` script. * `angular.js` — The non-minified `angular` script.
* `angular.min.js` — The minified `angular` script. * `angular.min.js` — The minified `angular` script.
* `angular-scenario.js` — The `angular` End2End test runner. * `angular-scenario.js` — The `angular` End2End test runner.
* `angular-ie-compat.js` — The Internet Explorer compatibility patch file. * `angular-ie-compat.js` — The Internet Explorer compatibility patch file.
* `docs/` — A directory that contains all of the files needed to run `docs.angularjs.org`. * `docs/` — A directory that contains all of the files needed to run `docs.angularjs.org`.
* `docs/index.html` — The main page for the documentation. * `docs/index.html` — The main page for the documentation.
* `docs/docs-scenario.html` — The End2End test runner for the documentation application. * `docs/docs-scenario.html` — The End2End test runner for the documentation application.
## Running a Local Development Web Server ## Running a Local Development Web Server
To debug or test code, it is often useful to have a local HTTP server. For this purpose, we have To debug or test code, it is often useful to have a local HTTP server. For this purpose, we have
made available a local web server based on Node.js. made available a local web server based on Node.js.
1. To start the web server, run: 1. To start the web server, run:
./nodeserver.sh ./nodeserver.sh
2. To access the local server, go to this website: 2. To access the local server, go to this website:
http://localhost:8000/ http://localhost:8000/
By default, it serves the contents of the `angular` project directory. By default, it serves the contents of the `angular` project directory.
<a name="unit-tests"></a> <a name="unit-tests"></a>
## Running the Unit Test Suite ## Running the Unit Test Suite
Our unit and integration tests are written with Jasmine and executed with JsTestDriver. To run the Our unit and integration tests are written with Jasmine and executed with JsTestDriver. To run the
tests: tests:
1. To start the JSTD server, run: 1. To start the JSTD server, run:
./server.sh ./server.sh
2. To capture one or more browsers, go to this website: 2. To capture one or more browsers, go to this website:
http://localhost:9876/ http://localhost:9876/
3. To trigger a test execution, run: 3. To trigger a test execution, run:
./test.sh ./test.sh
4. To automatically run the test suite each time one or more of the files in the project directory 4. To automatically run the test suite each time one or more of the files in the project directory
is changed, you can install `watchr` and then run: is changed, you can install `watchr` and then run:
watchr watchr.rb watchr watchr.rb
5. To view the output of each test run, you can tail this log file: 5. To view the output of each test run, you can tail this log file:
./logs/jstd.log ./logs/jstd.log
## Running the End2End Test Suite ## Running the End2End Test Suite
To run the End2End test suite: To run the End2End test suite:
1. Start the local web server. 1. Start the local web server.
2. In a browser, go to: 2. In a browser, go to:
http://localhost:8000/build/docs/docs-scenario.html http://localhost:8000/build/docs/docs-scenario.html
The tests are executed automatically. The tests are executed automatically.
<a name="H1_5"></a> <a name="H1_5"></a>
# Submitting Your Changes # Submitting Your Changes
To create and submit a change: To create and submit a change:
1. Create a new branch off the master for your changes: 1. Create a new branch off the master for your changes:
git branch my-fix-branch git branch my-fix-branch
2. Check out the branch: 2. Check out the branch:
git checkout my-fix-branch git checkout my-fix-branch
3. Create your patch, make sure to have plenty of tests (that pass). 3. Create your patch, make sure to have plenty of tests (that pass).
4. Commit your changes: 4. Commit your changes:
git commit -a git commit -a
5. Run JavaScript Lint and be sure to address all new warnings and errors: 5. Run JavaScript Lint and be sure to address all new warnings and errors:
rake lint rake lint
6. Push your branch to Github: 6. Push your branch to Github:
git push origin my-fix-branch git push origin my-fix-branch
7. In Github, send a pull request to `angular:master`. 7. In Github, send a pull request to `angular:master`.
8. When the patch is reviewed and merged, delete your branch and pull yours — and other — changes 8. When the patch is reviewed and merged, delete your branch and pull yours — and other — changes
from the main (upstream) repository: from the main (upstream) repository:
1. To delete the branch in Github, run: 1. To delete the branch in Github, run:
git push origin :my-fix-branch git push origin :my-fix-branch
2. To check out the master branch, run: 2. To check out the master branch, run:
git checkout master git checkout master
3. To delete a local branch, run: 3. To delete a local branch, run:
git branch -D my-fix-branch git branch -D my-fix-branch
4. To update your master with the latest upstream version, run: 4. To update your master with the latest upstream version, run:
git pull --ff upstream master git pull --ff upstream master
That's it! Thank you for your contribution! That's it! Thank you for your contribution!

View file

@ -3,27 +3,22 @@
@name Downloading @name Downloading
@description @description
# Including angular scripts from the angular server # Including angular scripts from the angular server
The quickest way to get started is to point your html `<script>` tag to a The quickest way to get started is to point your html `<script>` tag to a
<http://code.angularjs.org/> URL. This way, you don't have to download anything or maintain a <http://code.angularjs.org/> URL. This way, you don't have to download anything or maintain a
local copy. local copy.
There are two types of angular script URLs you can point to, one for development and one for There are two types of angular script URLs you can point to, one for development and one for
production: production:
* __angular-<version>.js__ — This is the human-readable, non-minified version, suitable for web * __angular-<version>.js__ — This is the human-readable, non-minified version, suitable for web
development. development.
* __angular-<version>.min.js__ — This is the minified version, which we strongly suggest you use in * __angular-<version>.min.js__ — This is the minified version, which we strongly suggest you use in
production. production.
To point your code to an angular script on the angular server, use the following template. This To point your code to an angular script on the angular server, use the following template. This
example points to (non-minified) version 0.9.12: example points to (non-minified) version 0.9.12:
<pre> <pre>
<!doctype html> <!doctype html>
<html xmlns:ng="http://angularjs.org"> <html xmlns:ng="http://angularjs.org">
@ -37,51 +32,40 @@ example points to (non-minified) version 0.9.12:
</pre> </pre>
# Downloading and hosting angular files locally # Downloading and hosting angular files locally
This option is for those who want to work with angular offline, or those who want to host the This option is for those who want to work with angular offline, or those who want to host the
angular files on their own servers. angular files on their own servers.
If you navigate to <http://code.angularjs.org/>, you'll see a directory listing with all of the If you navigate to <http://code.angularjs.org/>, you'll see a directory listing with all of the
angular versions since we started releasing versioned build artifacts (quite late in the project angular versions since we started releasing versioned build artifacts (quite late in the project
lifetime). Each directory contains all artifacts that we released for a particular version. lifetime). Each directory contains all artifacts that we released for a particular version.
Download the version you want and have fun. Download the version you want and have fun.
Each directory under <http://code.angularjs.org/> includes the following set of files: Each directory under <http://code.angularjs.org/> includes the following set of files:
* __`angular-<version>.js`__ — This file is non-obfuscated, non-minified, and human-readable by * __`angular-<version>.js`__ — This file is non-obfuscated, non-minified, and human-readable by
opening it it any editor or browser. In order to get better error messages during development, you opening it it any editor or browser. In order to get better error messages during development, you
should always use this non-minified angular script. should always use this non-minified angular script.
* __`angular-<version>.min.js`__ — This is a minified and obfuscated version of * __`angular-<version>.min.js`__ — This is a minified and obfuscated version of
`angular-<version>.js` created with the Closure compiler. Use this version for production in order `angular-<version>.js` created with the Closure compiler. Use this version for production in order
to minimize the size of the application that is downloaded by your user's browser. to minimize the size of the application that is downloaded by your user's browser.
* __`angular-<version>.tgz`__ — This is a tarball archive that contains all of the files released * __`angular-<version>.tgz`__ — This is a tarball archive that contains all of the files released
for this angular version. Use this file to get everything in a single download. for this angular version. Use this file to get everything in a single download.
* __`angular-ie-compat-<version>.js`__ — This is a special file that contains code and data * __`angular-ie-compat-<version>.js`__ — This is a special file that contains code and data
specifically tailored for getting Internet Explorer to work with angular. If you host your own copy specifically tailored for getting Internet Explorer to work with angular. If you host your own copy
of angular files, make sure that this file is available for download, and that it resides under the of angular files, make sure that this file is available for download, and that it resides under the
same parent path as `angular-<version>.js` or `angular-<version>.min.js`. same parent path as `angular-<version>.js` or `angular-<version>.min.js`.
* __`angular-mocks-<version>.js`__ — This file contains an implementation of mocks that makes * __`angular-mocks-<version>.js`__ — This file contains an implementation of mocks that makes
testing angular apps even easier. Your unit/integration test harness should load this file after testing angular apps even easier. Your unit/integration test harness should load this file after
`angular-<version>.js` is loaded. `angular-<version>.js` is loaded.
* __`angular-scenario-<version>.js`__ — This file is a very nifty JavaScript file that allows you * __`angular-scenario-<version>.js`__ — This file is a very nifty JavaScript file that allows you
to write and execute end-to-end tests for angular applications. to write and execute end-to-end tests for angular applications.
* __`docs-<version>`__ — this directory contains all the files that compose the * __`docs-<version>`__ — this directory contains all the files that compose the
<http://docs.angularjs.org/> documentation app. These files are handy to see the older version of <http://docs.angularjs.org/> documentation app. These files are handy to see the older version of
our docs, or even more importantly, view the docs offline. our docs, or even more importantly, view the docs offline.

View file

@ -3,49 +3,36 @@
@name FAQ @name FAQ
@description @description
#FAQ #FAQ
### Why is this project called "angular"? Why is the namespace called "ng"? ### Why is this project called "angular"? Why is the namespace called "ng"?
Because HTML has angular brackets and "ng" sounds like "angular". Because HTML has angular brackets and "ng" sounds like "angular".
### Is <angular/> an HTML5 tag? ### Is <angular/> an HTML5 tag?
No, <angular/> is not an HTML5 tag. angular is an orthogonal project to HTML5; you can use the two No, <angular/> is not an HTML5 tag. angular is an orthogonal project to HTML5; you can use the two
together. together.
### Is angular a {library, framework, DOM manipulation library, widget library, native plugin}? ### Is angular a {library, framework, DOM manipulation library, widget library, native plugin}?
No, angular is none of these. You don't call its functions, it does not call your functions, No, angular is none of these. You don't call its functions, it does not call your functions,
it does not provide a way to manipulate DOM, but does provide primitives to create UI projections it does not provide a way to manipulate DOM, but does provide primitives to create UI projections
of your data. There are lots of existing widget libraries which you can integrate with angular. of your data. There are lots of existing widget libraries which you can integrate with angular.
It is 100% JavaScript, 100% client side and compatible with both desktop and mobile browsers. It is 100% JavaScript, 100% client side and compatible with both desktop and mobile browsers.
### Do I need to worry about security holes in angular? ### Do I need to worry about security holes in angular?
Like with any technology, angular is not impervious to attack. angular does, however, provide Like with any technology, angular is not impervious to attack. angular does, however, provide
built-in protection from basic security holes including cross-site scripting and HTML injection built-in protection from basic security holes including cross-site scripting and HTML injection
attacks. angular does round-trip escaping on all strings for you. attacks. angular does round-trip escaping on all strings for you.
### Can I download the source, build, and host the angular environment locally? ### Can I download the source, build, and host the angular environment locally?
Yes. See instructions in {@link downloading}. Yes. See instructions in {@link downloading}.
### Is angular a templating system? ### Is angular a templating system?
At the highest level, angular does look like a just another templating system. But there is one At the highest level, angular does look like a just another templating system. But there is one
important reason why angular templating system is different and makes it very good fit for important reason why angular templating system is different and makes it very good fit for
application development: bidirectional data binding. The template is compiled on the browser and application development: bidirectional data binding. The template is compiled on the browser and
@ -53,59 +40,43 @@ the compilation step produces a live view. This means you, the developer, don't
code to constantly sync the view with the model and the model with the view as in other code to constantly sync the view with the model and the model with the view as in other
templating systems. templating systems.
### What browsers does angular work with? ### What browsers does angular work with?
Webkit-based browsers (Safari, Chrome, iPhone, Android, WebOS, BlackBerry 6), Firefox, IE6 and Webkit-based browsers (Safari, Chrome, iPhone, Android, WebOS, BlackBerry 6), Firefox, IE6 and
above. Note that CSS only works on IE7 and above. above. Note that CSS only works on IE7 and above.
### What's angular's performance like? ### What's angular's performance like?
angular takes ~300ms to load, render, and compile. In Chrome it uses about 2-5MB of memory. Your angular takes ~300ms to load, render, and compile. In Chrome it uses about 2-5MB of memory. Your
app's performance will vary depending on how many bindings you use. app's performance will vary depending on how many bindings you use.
### How big is the angular bootstrap JS file that I need to include? ### How big is the angular bootstrap JS file that I need to include?
The size of the library itself is < 50KB compressed and obfuscated. The size of the library itself is < 50KB compressed and obfuscated.
### Can I use the open-source Closure Library with angular? ### Can I use the open-source Closure Library with angular?
Yes, you can use widgets from the {@link http://code.google.com/closure/library Closure Library} Yes, you can use widgets from the {@link http://code.google.com/closure/library Closure Library}
in angular. in angular.
### Does angular use the jQuery library? ### Does angular use the jQuery library?
Yes, angular uses {@link http://jquery.com/ jQuery}, the open source DOM manipulation library. Yes, angular uses {@link http://jquery.com/ jQuery}, the open source DOM manipulation library.
If jQuery is not present in your script path, angular falls back on its own implementation of If jQuery is not present in your script path, angular falls back on its own implementation of
{@link api/angular.element jQuery lite}. If jQuery is present in the path, angular uses it to {@link api/angular.element jQuery lite}. If jQuery is present in the path, angular uses it to
manipulate the DOM. manipulate the DOM.
### What is testability like in angular? ### What is testability like in angular?
Very testable. It has an integrated dependency injection framework. See Very testable. It has an integrated dependency injection framework. See
{@link api/angular.service service} for details. {@link api/angular.service service} for details.
### How can I learn more about angular? ### How can I learn more about angular?
Watch the July 28, 2010 talk Watch the July 28, 2010 talk
"{@link http://www.youtube.com/watch?v=elvcgVSynRg| Angular: A Radically Different Way of Building "{@link http://www.youtube.com/watch?v=elvcgVSynRg| Angular: A Radically Different Way of Building
AJAX Apps}". AJAX Apps}".
### How is angular licensed? ### How is angular licensed?
The MIT License. The MIT License.

View file

@ -2,89 +2,69 @@
@name Getting Started @name Getting Started
@description @description
# Hello World! # Hello World!
A great way for you to get started with `angular` is to create the tradtional A great way for you to get started with `angular` is to create the tradtional
"Hello World!" app: "Hello World!" app:
1. In your favorite text editor, create an HTML file 1. In your favorite text editor, create an HTML file
(for example, `helloworld.html`). (for example, `helloworld.html`).
2. From the __Source__ box below, copy and paste the code into your HTML file. 2. From the __Source__ box below, copy and paste the code into your HTML file.
(Double-click on the source to easily select all.) (Double-click on the source to easily select all.)
3. Open the file in your web browser. 3. Open the file in your web browser.
<doc:example> <doc:example>
<doc:source> <doc:source>
Hello {{'World'}}! Hello {{'World'}}!
</doc:source> </doc:source>
</doc:example> </doc:example>
The resulting web page should look something like the following: The resulting web page should look something like the following:
<img class="center" src="img/helloworld.png" border="1" /> <img class="center" src="img/helloworld.png" border="1" />
Now let's take a closer look at that code, and see what is going on behind Now let's take a closer look at that code, and see what is going on behind
the scenes. the scenes.
The first line of interest defines the `ng` namespace, which makes The first line of interest defines the `ng` namespace, which makes
`angular` work across all browsers (especially important for IE): `angular` work across all browsers (especially important for IE):
<pre> <pre>
<html xmlns:ng="http://angularjs.org"> <html xmlns:ng="http://angularjs.org">
</pre> </pre>
The next line downloads the `angular` script, and instructs `angular` to process The next line downloads the `angular` script, and instructs `angular` to process
the entire HTML page when it is loaded: the entire HTML page when it is loaded:
<pre> <pre>
<script type="text/javascript" src="http://code.angularjs.org/angular-?.?.?.min.js" <script type="text/javascript" src="http://code.angularjs.org/angular-?.?.?.min.js"
ng:autobind></script> ng:autobind></script>
</pre> </pre>
(For details on what happens when `angular` processes an HTML page, (For details on what happens when `angular` processes an HTML page,
see {@link guide/dev_guide.bootstrap Bootstrap}.) see {@link guide/dev_guide.bootstrap Bootstrap}.)
Finally, this line in the `<body>` of the page is the template that describes Finally, this line in the `<body>` of the page is the template that describes
how to display our greeting in the UI: how to display our greeting in the UI:
<pre> <pre>
Hello {{'World'}}! Hello {{'World'}}!
</pre> </pre>
Note the use of the double curly brace markup (`{{ }}`) to bind the expression to Note the use of the double curly brace markup (`{{ }}`) to bind the expression to
the greeting text. Here the expression is the string literal 'World'. the greeting text. Here the expression is the string literal 'World'.
Next let's look at a more interesting example, that uses `angular` to Next let's look at a more interesting example, that uses `angular` to
bind a dynamic expression to our greeting text. bind a dynamic expression to our greeting text.
# Hello <angular/> World! # Hello <angular/> World!
This example demonstrates `angular`'s two-way data binding: This example demonstrates `angular`'s two-way data binding:
1. Edit the HTML file you created in the "Hello World!" example above. 1. Edit the HTML file you created in the "Hello World!" example above.
2. Replace the contents of `<body>` with the code from the __Source__ box below. 2. Replace the contents of `<body>` with the code from the __Source__ box below.
3. Refresh your browswer window. 3. Refresh your browswer window.
<doc:example> <doc:example>
<doc:source> <doc:source>
Your name: <input type="text" name="yourname" value="World"/> Your name: <input type="text" name="yourname" value="World"/>
@ -93,24 +73,18 @@ This example demonstrates `angular`'s two-way data binding:
</doc:source> </doc:source>
</doc:example> </doc:example>
After the refresh, the page should look something like this: After the refresh, the page should look something like this:
<img class="left" src="img/helloworld_2way.png" border="1" /> <img class="left" src="img/helloworld_2way.png" border="1" />
These are some of the important points to note from this example: These are some of the important points to note from this example:
* The text input {@link api/angular.widget widget} called `yourname` is bound to a model variable * The text input {@link api/angular.widget widget} called `yourname` is bound to a model variable
called `yourname`. called `yourname`.
* The double curly braces notation binds the `yourname` model to the greeting text. * The double curly braces notation binds the `yourname` model to the greeting text.
* You did not need to explicitly register an event listener or define an event handler for events! * You did not need to explicitly register an event listener or define an event handler for events!
Now try typing your name into the input box, and notice the immediate change to Now try typing your name into the input box, and notice the immediate change to
the displayed greeting. This demonstrates the concept of `angular`'s the displayed greeting. This demonstrates the concept of `angular`'s
{@link guide/dev_guide.templates.databinding bi-directional data binding}. Any changes to the input {@link guide/dev_guide.templates.databinding bi-directional data binding}. Any changes to the input
@ -119,70 +93,50 @@ reflected in the model (one direction), and any changes to the model are
reflected in the greeting text (the other direction). reflected in the greeting text (the other direction).
# Anatomy Of An Angular App # Anatomy Of An Angular App
This section describes the 3 parts of an angular app, and explains how they map to the This section describes the 3 parts of an angular app, and explains how they map to the
Model-View-Controller design pattern: Model-View-Controller design pattern:
## Templates ## Templates
Templates, which you write in HTML and CSS, serve as the View. You add elements, attributes, and Templates, which you write in HTML and CSS, serve as the View. You add elements, attributes, and
markup to HTML, which serve as instructions to the angular compiler. The angular compiler is fully markup to HTML, which serve as instructions to the angular compiler. The angular compiler is fully
extensible, meaning that with angular you can build your own declarative language on top of HTML! extensible, meaning that with angular you can build your own declarative language on top of HTML!
## Application Logic and Behavior ## Application Logic and Behavior
Application Logic and Behavior, which you define in JavaScript, serve as the Controller. With Application Logic and Behavior, which you define in JavaScript, serve as the Controller. With
angular (unlike with standard AJAX applications) you don't need to write additional listeners or angular (unlike with standard AJAX applications) you don't need to write additional listeners or
DOM manipulators, because they are built-in. This feature makes your application logic very easy to DOM manipulators, because they are built-in. This feature makes your application logic very easy to
write, test, maintain, and understand. write, test, maintain, and understand.
## Data ## Data
The Model is referenced from properties on {@link guide/dev_guide.scopes angular scope objects}. The Model is referenced from properties on {@link guide/dev_guide.scopes angular scope objects}.
The data in your model could be Javascript objects, arrays, or primitives, it doesn't matter. What The data in your model could be Javascript objects, arrays, or primitives, it doesn't matter. What
matters is that these are all referenced by the scope object. matters is that these are all referenced by the scope object.
Angular employs scopes to keep your data model and your UI in sync. Whenever something occurs to Angular employs scopes to keep your data model and your UI in sync. Whenever something occurs to
change the state of the model, angular immediately reflects that change in the UI, and vice versa. change the state of the model, angular immediately reflects that change in the UI, and vice versa.
The following illustration shows the parts of an angular application and how they work together: The following illustration shows the parts of an angular application and how they work together:
<img class="left" src="img/angular_parts.png" border="0" /> <img class="left" src="img/angular_parts.png" border="0" />
In addition, angular comes with a set of Services, which have the following properties: In addition, angular comes with a set of Services, which have the following properties:
* The services provided are very useful for building web applications. * The services provided are very useful for building web applications.
* You can extend and add application-specific behavior to services. * You can extend and add application-specific behavior to services.
* Services include Dependency-Injection, XHR, caching, URL routing, and browser abstraction. * Services include Dependency-Injection, XHR, caching, URL routing, and browser abstraction.
# Where To Go Next # Where To Go Next
* For explanations and examples of the angular concepts presented on this page, see the {@link * For explanations and examples of the angular concepts presented on this page, see the {@link
guide/index Developer Guide}. guide/index Developer Guide}.
* For additional hands-on examples of using `angular`, including more source code that you can * For additional hands-on examples of using `angular`, including more source code that you can
copy and paste into your own pages, take a look through the `angular` {@link cookbook/ Cookbook}. copy and paste into your own pages, take a look through the `angular` {@link cookbook/ Cookbook}.

View file

@ -2,20 +2,16 @@
@name Tutorial @name Tutorial
@description @description
A great way to get introduced to angular is to work through this tutorial, which walks you through A great way to get introduced to angular is to work through this tutorial, which walks you through
the construction of an angular web app. The app you will build is a catalog that displays a list of the construction of an angular web app. The app you will build is a catalog that displays a list of
Android devices, lets you filter the list to see only devices that interest you, and then view Android devices, lets you filter the list to see only devices that interest you, and then view
details for any device. details for any device.
<img src="img/tutorial/catalog_screen.png"> <img src="img/tutorial/catalog_screen.png">
As you work through this tutorial, you will learn how angular makes browsers smarter — without the As you work through this tutorial, you will learn how angular makes browsers smarter — without the
use of extensions or plugins. use of extensions or plugins.
* You will see examples of how to use client-side data binding and dependency injection to build * You will see examples of how to use client-side data binding and dependency injection to build
dynamic views of data that change immediately in response to user actions. dynamic views of data that change immediately in response to user actions.
* You will see how angular creates listeners on your data without the need for DOM manipulation. * You will see how angular creates listeners on your data without the need for DOM manipulation.
@ -23,13 +19,10 @@ dynamic views of data that change immediately in response to user actions.
* You will learn how to use angular services to make common web tasks, such as getting data into * You will learn how to use angular services to make common web tasks, such as getting data into
your app, easier. your app, easier.
And all of this works in any browser without modifications! And all of this works in any browser without modifications!
When you finish the tutorial you will be able to: When you finish the tutorial you will be able to:
* Create a dynamic application that works in any browser * Create a dynamic application that works in any browser
* Define the differences between angular and common JavaScript frameworks * Define the differences between angular and common JavaScript frameworks
* Understand how data binding works in angular * Understand how data binding works in angular
@ -37,12 +30,10 @@ When you finish the tutorial you will be able to:
* Create and run tests * Create and run tests
* Identify resources for learning more about angular * Identify resources for learning more about angular
The tutorial is will guide you through the process of building a simple application, including The tutorial is will guide you through the process of building a simple application, including
writing and running unit and end-to-end tests, and will allow you to experiment with angular and writing and running unit and end-to-end tests, and will allow you to experiment with angular and
the application through experiments suggested at the end of each step. the application through experiments suggested at the end of each step.
You can go through the whole tutorial in a couple of hours or you may want to spend a pleasant day You can go through the whole tutorial in a couple of hours or you may want to spend a pleasant day
really digging into it. If you're looking for a shorter introduction to angular, check out the really digging into it. If you're looking for a shorter introduction to angular, check out the
{@link misc/started Getting Started} document. {@link misc/started Getting Started} document.
@ -53,23 +44,14 @@ really digging into it. If you're looking for a shorter introduction to angular,
# Working with the code # Working with the code
There are two ways that you can you follow this tutorial and hack on the code, both available on There are two ways that you can you follow this tutorial and hack on the code, both available on
Mac/Linux or Windows environment. The first work flow uses Git versioning system for source code Mac/Linux or Windows environment. The first work flow uses Git versioning system for source code
management, the second work flow doesn't depend on any source control system and instead uses management, the second work flow doesn't depend on any source control system and instead uses
scripts to copy snapshots of project files into your workspace (`sandbox`) directory. Choose the scripts to copy snapshots of project files into your workspace (`sandbox`) directory. Choose the
one you prefer: one you prefer:
<doc:tutorial-instructions show="true"> <doc:tutorial-instructions show="true">
<doc:tutorial-instruction id="git-mac" title="Git on Mac/Linux"> <doc:tutorial-instruction id="git-mac" title="Git on Mac/Linux">
<ol> <ol>
@ -95,7 +77,6 @@ server.</p></li>
</ol> </ol>
</doc:tutorial-instruction> </doc:tutorial-instruction>
<doc:tutorial-instruction id="git-win" title="Git on Windows"> <doc:tutorial-instruction id="git-win" title="Git on Windows">
<ol> <ol>
<li><p>Verify that you have <a href="http://java.com/">Java</a> installed and that the <li><p>Verify that you have <a href="http://java.com/">Java</a> installed and that the
@ -123,7 +104,6 @@ href="http://node-js.prcn.co.cc/">pre-compiled binaries</a>, unzip them and add
</ol> </ol>
</doc:tutorial-instruction> </doc:tutorial-instruction>
<doc:tutorial-instruction id="ss-mac" title="Snapshots on Mac/Linux"> <doc:tutorial-instruction id="ss-mac" title="Snapshots on Mac/Linux">
<ol> <ol>
<li><p>Verify that you have <a href="http://java.com/">Java</a> installed by running the <li><p>Verify that you have <a href="http://java.com/">Java</a> installed by running the
@ -144,7 +124,6 @@ server.</p></li>
</ol> </ol>
</doc:tutorial-instruction> </doc:tutorial-instruction>
<doc:tutorial-instruction id="ss-win" title="Snapshots on Windows"> <doc:tutorial-instruction id="ss-win" title="Snapshots on Windows">
<ol> <ol>
<li><p>Verify that you have <a href="http://java.com/">Java</a> installed and that the <li><p>Verify that you have <a href="http://java.com/">Java</a> installed and that the
@ -167,9 +146,6 @@ href="http://node-js.prcn.co.cc/">pre-compiled binaries</a>, unzip them and add
</doc:tutorial-instruction> </doc:tutorial-instruction>
</doc:tutorial-instructions> </doc:tutorial-instructions>
For either work flow you'll also need a web browser and your favorite text editor. For either work flow you'll also need a web browser and your favorite text editor.
Let's get going with {@link step_00 step 0}. Let's get going with {@link step_00 step 0}.

View file

@ -2,19 +2,14 @@
@name Tutorial: 0 - angular-seed @name Tutorial: 0 - angular-seed
@description @description
<ul doc:tutorial-nav="0"></ul> <ul doc:tutorial-nav="0"></ul>
You are now ready to build the phonecat application. In this step, you will become familiar with You are now ready to build the phonecat application. In this step, you will become familiar with
the most important source code files, learn how to start the development servers bundled with the most important source code files, learn how to start the development servers bundled with
angular-seed, and run the application in the browser. angular-seed, and run the application in the browser.
<doc:tutorial-instructions show="true"> <doc:tutorial-instructions show="true">
<doc:tutorial-instruction id="git-mac" title="Git on Mac/Linux"> <doc:tutorial-instruction id="git-mac" title="Git on Mac/Linux">
<ol> <ol>
@ -25,7 +20,6 @@ angular-seed, and run the application in the browser.
the number of the step you are on. This will cause any changes you made within the number of the step you are on. This will cause any changes you made within
your working directory to be lost.</p></li> your working directory to be lost.</p></li>
<li>To see the app running in a browser, do one of the following: <li>To see the app running in a browser, do one of the following:
<ul> <ul>
<li><b>For node.js users:</b> <li><b>For node.js users:</b>
@ -50,8 +44,6 @@ directory.</li>
</doc:tutorial-instruction> </doc:tutorial-instruction>
<doc:tutorial-instruction id="git-win" title="Git on Windows"> <doc:tutorial-instruction id="git-win" title="Git on Windows">
<ol> <ol>
<li><p>Open msysGit bash and run this command (in angular-phonecat directory):</p> <li><p>Open msysGit bash and run this command (in angular-phonecat directory):</p>
@ -84,8 +76,6 @@ directory.</li>
</doc:tutorial-instruction> </doc:tutorial-instruction>
<doc:tutorial-instruction id="ss-mac" title="Snapshots on Mac/Linux"> <doc:tutorial-instruction id="ss-mac" title="Snapshots on Mac/Linux">
<ol> <ol>
<li><p>In angular-phonecat directory, run this command:</p> <li><p>In angular-phonecat directory, run this command:</p>
@ -118,8 +108,6 @@ href="http://localhost:8000/app/index.html">http://localhost:8000/app/index.html
</doc:tutorial-instruction> </doc:tutorial-instruction>
<doc:tutorial-instruction id="ss-win" title="Snapshots on Windows"> <doc:tutorial-instruction id="ss-win" title="Snapshots on Windows">
<ol> <ol>
<li><p>Open windows command line and run this command (in angular-phonecat directory):</p> <li><p>Open windows command line and run this command (in angular-phonecat directory):</p>
@ -153,15 +141,11 @@ href="http://localhost:8000/app/index.html">http://localhost:8000/app/index.html
</doc:tutorial-instructions> </doc:tutorial-instructions>
You can now see the page in your browser. It's not very exciting, but that's OK. You can now see the page in your browser. It's not very exciting, but that's OK.
The static HTML page that displays "Nothing here yet!" was constructed with the HTML code shown The static HTML page that displays "Nothing here yet!" was constructed with the HTML code shown
below. The code contains some key angular elements that we will need going forward. below. The code contains some key angular elements that we will need going forward.
__`app/index.html`:__ __`app/index.html`:__
<pre> <pre>
<!doctype html> <!doctype html>
@ -173,10 +157,8 @@ __`app/index.html`:__
</head> </head>
<body> <body>
Nothing here yet! Nothing here yet!
<script src="lib/angular/angular.js" ng:autobind></script> <script src="lib/angular/angular.js" ng:autobind></script>
</body> </body>
</html> </html>
@ -184,73 +166,51 @@ __`app/index.html`:__
## What is the code doing? ## What is the code doing?
* xmlns declaration * xmlns declaration
<html xmlns:ng="http://angularjs.org"> <html xmlns:ng="http://angularjs.org">
This `xmlns` declaration for the `ng` namespace must be specified in all angular applications in This `xmlns` declaration for the `ng` namespace must be specified in all angular applications in
order to make angular work with XHTML and IE versions older than 9 (regardless of whether you are order to make angular work with XHTML and IE versions older than 9 (regardless of whether you are
using XHTML or HTML). using XHTML or HTML).
* angular script tag * angular script tag
<script src="lib/angular/angular.js" ng:autobind> <script src="lib/angular/angular.js" ng:autobind>
This single line of code is all that is needed to bootstrap an angular application. This single line of code is all that is needed to bootstrap an angular application.
The code downloads the `angular.js` script and registers a callback that will be executed by the The code 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 browser when the containing HTML page is fully downloaded. When the callback is executed, angular
looks for the {@link api/angular.directive.ng:autobind ng:autobind} attribute. If angular finds looks for the {@link api/angular.directive.ng:autobind ng:autobind} attribute. If angular finds
`ng:autobind`, it creates a root scope for the application and associates it with the `<html>` `ng:autobind`, it creates a root scope for the application and associates it with the `<html>`
element of the template: element of the template:
<img src="img/tutorial/tutorial_00_final.png">
<img src="img/tutorial/tutorial_00_final.png"/>
As you will see shortly, everything in angular is evaluated within a scope. We'll learn more As you will see shortly, everything in angular is evaluated within a scope. We'll learn more
about this in the next steps. about this in the next steps.
## What are all these files in my working directory? ## What are all these files in my working directory?
Most of the files in your working directory come from the {@link Most of the files in your working directory come from the {@link
https://github.com/angular/angular-seed angular-seed project} which is typically used to bootstrap https://github.com/angular/angular-seed angular-seed project} which is typically used to bootstrap
new angular projects. The seed project includes the latest angular libraries, test libraries, new angular projects. The seed project includes the latest angular libraries, test libraries,
scripts and a simple example app, all pre-configured for developing a typical web app. scripts and a simple example app, all pre-configured for developing a typical web app.
For the purposes of this tutorial, we modified the angular-seed with the following changes: For the purposes of this tutorial, we modified the angular-seed with the following changes:
* Removed the example app * Removed the example app
* Added phone images to `app/img/phones` * Added phone images to `app/img/phones`
* Added phone data files (JSON) to `app/phones` * Added phone data files (JSON) to `app/phones`
# Summary # Summary
Now let's go to step 1 and add some content to the web app. Now let's go to step 1 and add some content to the web app.
<ul doc:tutorial-nav="0"></ul> <ul doc:tutorial-nav="0"></ul>

View file

@ -2,34 +2,24 @@
@name Tutorial: 1 - Static Template @name Tutorial: 1 - Static Template
@description @description
<ul doc:tutorial-nav="1"></ul> <ul doc:tutorial-nav="1"></ul>
In order to illustrate how angular enhances standard HTML, you will create a purely *static* HTML In order to illustrate how angular enhances standard HTML, you will create a purely *static* HTML
page and then examine how we can turn this HTML code into a template that angular will use to page and then examine how we can turn this HTML code into a template that angular will use to
dynamically display the same result with any set of data. dynamically display the same result with any set of data.
In this step you will add some basic information about two cell phones to an HTML page. In this step you will add some basic information about two cell phones to an HTML page.
<doc:tutorial-instructions step="1" show="true"></doc:tutorial-instructions> <doc:tutorial-instructions step="1" show="true"></doc:tutorial-instructions>
The page now contains a list with information about two phones. The page now contains a list with information about two phones.
The most important changes are listed below. You can see the full diff on {@link The most important changes are listed below. You can see the full diff on {@link
https://github.com/angular/angular-phonecat/compare/step-0...step-1 GitHub}: https://github.com/angular/angular-phonecat/compare/step-0...step-1 GitHub}:
__`app/index.html`:__ __`app/index.html`:__
<pre> <pre>
... ...
@ -51,27 +41,17 @@ __`app/index.html`:__
</pre> </pre>
# Experiments # Experiments
* Try adding more static HTML to `index.html`. For example: * Try adding more static HTML to `index.html`. For example:
<p>Total number of phones: 2</p> <p>Total number of phones: 2</p>
# Summary # Summary
This addition to your app uses static HTML to display the list. Now, let's go to step 2 to learn This addition to your app uses static HTML to display the list. Now, let's go to step 2 to learn
how to use angular to dynamically generate the same list. how to use angular to dynamically generate the same list.
<ul doc:tutorial-nav="1"></ul> <ul doc:tutorial-nav="1"></ul>

View file

@ -2,50 +2,36 @@
@name Tutorial: 2 - Angular Template @name Tutorial: 2 - Angular Template
@description @description
<ul doc:tutorial-nav="2"></ul> <ul doc:tutorial-nav="2"></ul>
Now it's time to make this web page dynamic with angular. We'll also add a test that verifies the Now it's time to make this web page dynamic with angular. We'll also add a test that verifies the
code for the controller we are going to add. code for the controller we are going to add.
There are many ways to structure the code for an application. With angular, we encourage the use of There are many ways to structure the code for an application. With angular, we encourage the use of
{@link http://en.wikipedia.org/wiki/ModelViewController the MVC design pattern} to decouple the {@link http://en.wikipedia.org/wiki/ModelViewController the MVC design pattern} to decouple the
code and separate concerns. With that in mind, let's use a little angular and JavaScript to add code and separate concerns. With that in mind, let's use a little angular and JavaScript to add
model, view, and controller components to our app. model, view, and controller components to our app.
<doc:tutorial-instructions step="2"></doc:tutorial-instructions> <doc:tutorial-instructions step="2"></doc:tutorial-instructions>
The app now contains a list with 3 phones. The app now contains a list with 3 phones.
The most important changes are listed below. You can see the full diff on {@link The most important changes are listed below. You can see the full diff on {@link
https://github.com/angular/angular-phonecat/compare/step-1...step-2 GitHub}: https://github.com/angular/angular-phonecat/compare/step-1...step-2 GitHub}:
## Template for the View ## Template for the View
The __view__ component is constructed by angular from this template: The __view__ component is constructed by angular from this template:
__`app/index.html`:__ __`app/index.html`:__
<pre> <pre>
... ...
<body ng:controller="PhoneListCtrl"> <body ng:controller="PhoneListCtrl">
<ul> <ul>
<li ng:repeat="phone in phones"> <li ng:repeat="phone in phones">
{{phone.name}} {{phone.name}}
@ -53,27 +39,22 @@ __`app/index.html`:__
</li> </li>
</ul> </ul>
<script src="lib/angular/angular.js" ng:autobind></script> <script src="lib/angular/angular.js" ng:autobind></script>
<script src="js/controllers.js"></script> <script src="js/controllers.js"></script>
</body> </body>
</html> </html>
</pre> </pre>
We replaced the hard-coded phone list with the {@link api/angular.widget.@ng:repeat ng:repeat We replaced the hard-coded phone list with the {@link api/angular.widget.@ng:repeat ng:repeat
widget} and two {@link guide/dev_guide.expressions angular expressions} enclosed in curly braces: widget} and two {@link guide/dev_guide.expressions angular expressions} enclosed in curly braces:
`{{phone.name}}` and `{{phone.snippet}}`: `{{phone.name}}` and `{{phone.snippet}}`:
* The `ng:repeat="phone in phones"` statement in the `<li>` tag is an angular repeater. It tells * 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 angular to create a `<li>` element for each phone in the phones list, using the first `<li>` tag as
the template. the template.
<img src="img/tutorial/tutorial_02_final.png"> <img src="img/tutorial/tutorial_02_final.png">
* The curly braces around `phone.name` and `phone.snippet` are an example of {@link * The curly braces around `phone.name` and `phone.snippet` are an example of {@link
guide/dev_guide.compiler.markup angular markup}. The curly markup is shorthand for the angular guide/dev_guide.compiler.markup angular markup}. The curly markup is shorthand for the angular
directive {@link api/angular.directive.ng:bind ng:bind}. The `ng:bind` directives indicate to directive {@link api/angular.directive.ng:bind ng:bind}. The `ng:bind` directives indicate to
@ -83,15 +64,11 @@ of the model through the HTML template. This means that whenever the model chang
refreshes the appropriate binding points, which updates the view. refreshes the appropriate binding points, which updates the view.
## Model and Controller ## Model and Controller
The data __model__ (a simple array of phones in object literal notation) is instantiated within The data __model__ (a simple array of phones in object literal notation) is instantiated within
the __controller__ function (`PhoneListCtrl`): the __controller__ function (`PhoneListCtrl`):
__`app/js/controllers.js`:__ __`app/js/controllers.js`:__
<pre> <pre>
function PhoneListCtrl() { function PhoneListCtrl() {
@ -107,22 +84,16 @@ function PhoneListCtrl() {
Although the controller is not yet doing very much controlling, it is playing a crucial role. By Although the controller is not yet doing very much controlling, it is playing a crucial role. By
providing context for our data model, the controller allows us to establish data-binding between providing context for our data model, the controller allows us to establish data-binding between
the model and the view. Note in the following how we connected the dots between our presentation, the model and the view. Note in the following how we connected the dots between our presentation,
data, and logic components: data, and logic components:
* The name of our controller function (in the JavaScript file `controllers.js`) matches the {@link * The name of our controller function (in the JavaScript file `controllers.js`) matches the {@link
api/angular.directive.ng:controller ng:controller} directive in the `<body>` tag (`PhoneListCtrl`). api/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 * 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. points are located within the block bounded by the `<body ng:controller="PhoneListCtrl">` tag.
Angular scopes are a crucial concept in angular; you can think of scopes as the glue that makes the Angular scopes are a crucial concept in angular; you can think of scopes as the glue that makes the
template, model and controller all work together. Angular uses scopes, along with the information template, model and controller all work together. Angular uses scopes, along with the information
contained in the template, data model, and controller, to keep the model and view separated but in contained in the template, data model, and controller, to keep the model and view separated but in
@ -131,23 +102,17 @@ reflected in the model. To learn more about angular scopes, see the {@link api/a
angular scope documentation}. angular scope documentation}.
## Tests ## Tests
The "Angular way" makes it easy for us to test as we develop; the unit test for your newly created The "Angular way" makes it easy for us to test as we develop; the unit test for your newly created
controller looks as follows: controller looks as follows:
__`test/unit/controllersSpec.js`:__ __`test/unit/controllersSpec.js`:__
<pre> <pre>
describe('PhoneCat controllers', function() { describe('PhoneCat controllers', function() {
describe('PhoneListCtrl', function() { describe('PhoneListCtrl', function() {
it('should create "phones" model with 3 phones', function() { it('should create "phones" model with 3 phones', function() {
var ctrl = new PhoneListCtrl(); var ctrl = new PhoneListCtrl();
expect(ctrl.phones.length).toBe(3); expect(ctrl.phones.length).toBe(3);
@ -156,105 +121,76 @@ describe('PhoneCat controllers', function() {
}); });
</pre> </pre>
Ease of testing is another cornerstone of angular's design philosophy. All we are doing here is Ease of testing is another cornerstone of angular's design philosophy. All we are doing here is
showing how easy it is to create a unit test. The test verifies that we have 3 records in the showing how easy it is to create a unit test. The test verifies that we have 3 records in the
phones array. phones array.
Angular developers prefer the syntax of Jasmine's Behavior-driven Development (BDD) framework when Angular developers prefer the syntax of Jasmine's Behavior-driven Development (BDD) framework when
writing tests. Although Jasmine is not required by angular, we used it to write all tests in this writing tests. Although Jasmine is not required by angular, we used it to write all tests in this
tutorial. You can learn about Jasmine on the {@link http://pivotal.github.com/jasmine/ Jasmine home 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}. page} and on the {@link https://github.com/pivotal/jasmine/wiki Jasmine wiki}.
The angular-seed project is pre-configured to run all unit tests using {@link The angular-seed project is pre-configured to run all unit tests using {@link
http://code.google.com/p/js-test-driver/ JsTestDriver}. To run the test, do the following: http://code.google.com/p/js-test-driver/ JsTestDriver}. To run the test, do the following:
1. In a _separate_ terminal window or tab, go to the `angular-phonecat` directory and run 1. In a _separate_ terminal window or tab, go to the `angular-phonecat` directory and run
`./scripts/test-server.sh` to start the test web server. `./scripts/test-server.sh` to start the test web server.
2. Open a new browser tab or window and navigate to {@link http://localhost:9876}. 2. Open a new browser tab or window and navigate to {@link http://localhost:9876}.
3. Choose "Capture this browser in strict mode". 3. Choose "Capture this browser in strict mode".
At this point, you can leave this tab open and forget about it. JsTestDriver will use it to At this point, you can leave this tab open and forget about it. JsTestDriver will use it to
execute the tests and report the results in the terminal. execute the tests and report the results in the terminal.
4. Execute the test by running `./scripts/test.sh` 4. Execute the test by running `./scripts/test.sh`
You should see the following or similar output: You should see the following or similar output:
Chrome: Runner reset. Chrome: Runner reset.
. .
Total 1 tests (Passed: 1; Fails: 0; Errors: 0) (2.00 ms) Total 1 tests (Passed: 1; Fails: 0; Errors: 0) (2.00 ms)
Chrome 11.0.696.57 Mac OS: Run 1 tests (Passed: 1; Fails: 0; Errors 0) (2.00 ms) Chrome 11.0.696.57 Mac OS: Run 1 tests (Passed: 1; Fails: 0; Errors 0) (2.00 ms)
Yay! The test passed! Or not... Yay! The test passed! Or not...
Note: If you see errors after you run the test, close the browser tab and go back to the terminal Note: If you see errors after you run the test, close the browser tab and go back to the terminal
and kill the script, then repeat the procedure above. and kill the script, then repeat the procedure above.
# Experiments # Experiments
* Add another binding to `index.html`. For example: * Add another binding to `index.html`. For example:
<p>Total number of phones: {{phones.length}}</p> <p>Total number of phones: {{phones.length}}</p>
* Create a new model property in the controller and bind to it from the template. For example: * Create a new model property in the controller and bind to it from the template. For example:
this.hello = "Hello, World!" this.hello = "Hello, World!"
Refresh your browser to make sure it says, "Hello, World!" Refresh your browser to make sure it says, "Hello, World!"
* Create a repeater that constructs a simple table: * Create a repeater that constructs a simple table:
<table> <table>
<tr><th>row number</th></tr> <tr><th>row number</th></tr>
<tr ng:repeat="i in [0, 1, 2, 3, 4, 5, 6, 7]"><td>{{i}}</td></tr> <tr ng:repeat="i in [0, 1, 2, 3, 4, 5, 6, 7]"><td>{{i}}</td></tr>
</table> </table>
Now, make the list 1-based by incrementing `i` by one in the binding: Now, make the list 1-based by incrementing `i` by one in the binding:
<table> <table>
<tr><th>row number</th></tr> <tr><th>row number</th></tr>
<tr ng:repeat="i in [0, 1, 2, 3, 4, 5, 6, 7]"><td>{{i+1}}</td></tr> <tr ng:repeat="i in [0, 1, 2, 3, 4, 5, 6, 7]"><td>{{i+1}}</td></tr>
</table> </table>
* Make the unit test fail by changing the `toBe(3)` statement to `toBe(4)`, and rerun the * Make the unit test fail by changing the `toBe(3)` statement to `toBe(4)`, and rerun the
`./scripts/test.sh` script. `./scripts/test.sh` script.
# Summary # Summary
You now have a dynamic app that features separate model, view, and controller components, and You now have a dynamic app that features separate model, view, and controller components, and
you're testing as you go. Now, let's go to step 3 to learn how to add full text search to the app. you're testing as you go. Now, let's go to step 3 to learn how to add full text search to the app.
<ul doc:tutorial-nav="2"></ul> <ul doc:tutorial-nav="2"></ul>

View file

@ -2,53 +2,38 @@
@name Tutorial: 3 - Filtering Repeaters @name Tutorial: 3 - Filtering Repeaters
@description @description
<ul doc:tutorial-nav="3"></ul> <ul doc:tutorial-nav="3"></ul>
We did a lot of work in laying a foundation for the app in the last step, so now we'll do something We did a lot of work in laying a foundation for the app in the last step, so now we'll do something
simple, and add full text search (yes, it will be simple!). We will also write an end-to-end test, simple, and add full text search (yes, it will be simple!). 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 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. quickly detects regressions.
<doc:tutorial-instructions step="3"></doc:tutorial-instructions> <doc:tutorial-instructions step="3"></doc:tutorial-instructions>
The app now has a search box. The phone list on the page changes depending on what a user types The app now has a search box. The phone list on the page changes depending on what a user types
into the search box. into the search box.
The most important changes are listed below. You can see the full diff on {@link The most important changes are listed below. You can see the full diff on {@link
https://github.com/angular/angular-phonecat/compare/step-2...step-3 https://github.com/angular/angular-phonecat/compare/step-2...step-3
GitHub}: GitHub}:
## Controller ## Controller
We made no changes to the controller. We made no changes to the controller.
## Template ## Template
__`app/index.html`:__ __`app/index.html`:__
<pre> <pre>
... ...
Fulltext Search: <input name="query"/> Fulltext Search: <input name="query"/>
<ul class="phones"> <ul class="phones">
<li ng:repeat="phone in phones.$filter(query)"> <li ng:repeat="phone in phones.$filter(query)">
{{phone.name}} {{phone.name}}
@ -58,70 +43,54 @@ __`app/index.html`:__
... ...
</pre> </pre>
We added a standard HTML `<input>` tag and use angular's {@link api/angular.Array.filter $filter} We added a standard HTML `<input>` tag and use angular's {@link api/angular.Array.filter $filter}
function to process the input for the `ng:repeater`. function to process the input for the `ng:repeater`.
This lets a user enter search criteria and immediately see the effects of their search on the phone This lets a user enter search criteria and immediately see the effects of their search on the phone
list. This new code demonstrates the following: list. This new code demonstrates the following:
* Data-binding. This is one of the core features in angular. When the page loads, angular binds the * 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. name of the input box to a variable of the same name in the data model and keeps the two in sync.
In this code, the data that a user types into the input box (named __`query`__) is immediately In this code, the data that a user types into the input box (named __`query`__) is immediately
available as a filter input in the list repeater (`phone in phones.$filter(`__`query`__`)`). When available as a filter input in the list repeater (`phone in phones.$filter(`__`query`__`)`). When
changes to the data model cause the repeater's input to change, the repeater efficiently updates changes to the data model cause the repeater's input to change, the repeater efficiently updates
the DOM to reflect the current state of the model. the DOM to reflect the current state of the model.
<img src="img/tutorial/tutorial_03_final.png"> <img src="img/tutorial/tutorial_03_final.png">
* Use of `$filter`. The {@link api/angular.Array.filter $filter} method, uses the `query` value, to * Use of `$filter`. The {@link api/angular.Array.filter $filter} method, uses the `query` value, to
create a new array that contains only those records that match the `query`. create a new array that contains only those records that match the `query`.
`ng:repeat` automatically updates the view in response to the changing number of phones returned `ng:repeat` automatically updates the view in response to the changing number of phones returned
by the `$filter`. The process is completely transparent to the developer. by the `$filter`. The process is completely transparent to the developer.
## Test ## Test
In step 2, we learned how to write and run unit tests. Unit tests are perfect for testing In step 2, we learned how to write and run unit tests. Unit tests are perfect for testing
controllers and other components of our application written in JavaScript, but they can't easily controllers and other components of our application written in JavaScript, but they can't easily
test DOM manipulation or the wiring of our application. For these, an end-to-end test is a much test DOM manipulation or the wiring of our application. For these, an end-to-end test is a much
better choice. better choice.
The search feature was fully implemented via templates and data-binding, so we'll write our first The search feature was fully implemented via templates and data-binding, so we'll write our first
end-to-end test, to verify that the feature works. end-to-end test, to verify that the feature works.
__`test/e2e/scenarios.js`:__ __`test/e2e/scenarios.js`:__
<pre> <pre>
describe('PhoneCat App', function() { describe('PhoneCat App', function() {
describe('Phone list view', function() { describe('Phone list view', function() {
beforeEach(function() { beforeEach(function() {
browser().navigateTo('../../app/index.html'); browser().navigateTo('../../app/index.html');
}); });
it('should filter the phone list as user types into the search box', function() { it('should filter the phone list as user types into the search box', function() {
expect(repeater('.phones li').count()).toBe(3); expect(repeater('.phones li').count()).toBe(3);
input('query').enter('nexus'); input('query').enter('nexus');
expect(repeater('.phones li').count()).toBe(1); expect(repeater('.phones li').count()).toBe(1);
input('query').enter('motorola'); input('query').enter('motorola');
expect(repeater('.phones li').count()).toBe(2); expect(repeater('.phones li').count()).toBe(2);
}); });
@ -129,64 +98,48 @@ describe('PhoneCat App', function() {
}); });
</pre> </pre>
Even though the syntax of this test looks very much like our controller unit test written with Even though the syntax of this test looks very much like our controller unit test written with
Jasmine, the end-to-end test uses APIs of {@link Jasmine, the end-to-end test uses APIs of {@link
https://docs.google.com/document/d/11L8htLKrh6c92foV71ytYpiKkeKpM4_a5-9c3HywfIc/edit?hl=en&pli=1# https://docs.google.com/document/d/11L8htLKrh6c92foV71ytYpiKkeKpM4_a5-9c3HywfIc/edit?hl=en&pli=1#
angular's end-to-end test runner}. angular's end-to-end test runner}.
To run the end-to-end test, open the following in a new browser tab: To run the end-to-end test, open the following in a new browser tab:
* node.js users: {@link http://localhost:8000/test/e2e/runner.html} * node.js users: {@link http://localhost:8000/test/e2e/runner.html}
* users with other http servers: * users with other http servers:
`http://localhost:[port-number]/[context-path]/test/e2e/runner.html` `http://localhost:[port-number]/[context-path]/test/e2e/runner.html`
* casual reader: {@link http://angular.github.com/angular-phonecat/step-3/test/e2e/runner.html} * casual reader: {@link http://angular.github.com/angular-phonecat/step-3/test/e2e/runner.html}
This test verifies that the search box and the repeater are correctly wired together. Notice how This test verifies that the search box and the repeater are correctly wired together. Notice how
easy it is to write end-to-end tests in angular. Although this example is for a simple test, it easy it is to write end-to-end tests in angular. Although this example is for a simple test, it
really is that easy to set up any functional, readable, end-to-end test. really is that easy to set up any functional, readable, end-to-end test.
# Experiments # Experiments
* Display the current value of the `query` model by adding a `{{query}}` binding into the * Display the current value of the `query` model by adding a `{{query}}` binding into the
`index.html` template, and see how it changes when you type in the input box. `index.html` template, and see how it changes when you type in the input box.
* Let's see how we can get the current value of the `query` model to appear in the HTML page title. * Let's see how we can get the current value of the `query` model to appear in the HTML page title.
You might think you could just add the {{query}} to the title tag element as follows: You might think you could just add the {{query}} to the title tag element as follows:
<title>Google Phone Gallery: {{query}}</title> <title>Google Phone Gallery: {{query}}</title>
However, when you reload the page, you won't see the expected result. This is because the "query" However, when you reload the page, you won't see the expected result. This is because the "query"
model lives in the scope defined by the body element: model lives in the scope defined by the body element:
<body ng:controller="PhoneListCtrl"> <body ng:controller="PhoneListCtrl">
If you want to bind to the query model from the `<title>` element, you must __move__ the If you want to bind to the query model from the `<title>` element, you must __move__ the
`ng:controller` declaration to the HTML element because it is the common parent of both the body `ng:controller` declaration to the HTML element because it is the common parent of both the body
and title elements: and title elements:
<html ng:controller="PhoneListCtrl"> <html ng:controller="PhoneListCtrl">
Be sure to *remove* the `ng:controller` declaration from the body element. Be sure to *remove* the `ng:controller` declaration from the body element.
* Add the following end-to-end test into the `describe` block within `test/e2e/scenarios.js`: * Add the following end-to-end test into the `describe` block within `test/e2e/scenarios.js`:
<pre> <pre>
it('should display the current filter value within an element with id "status"', it('should display the current filter value within an element with id "status"',
function() { function() {
@ -201,30 +154,21 @@ and title elements:
}); });
</pre> </pre>
Refresh the browser tab with end-to-end test runner to see the test fail. Now add a `div` or `p` Refresh the browser tab with end-to-end test runner to see the test fail. Now add a `div` or `p`
element with `id` `"status"` and content with the `query` binding into the `index.html` template to element with `id` `"status"` and content with the `query` binding into the `index.html` template to
make the test pass. make the test pass.
* Add a `pause()` statement into an end-to-end test and rerun it. You'll see the runner pausing, * Add a `pause()` statement into an end-to-end test and rerun it. You'll see the runner pausing,
giving you the opportunity to explore the state of your application displayed in the browser. The giving you the opportunity to explore the state of your application displayed in the browser. The
app is live! Change the search query to prove it. This is great for troubleshooting end-to-end app is live! Change the search query to prove it. This is great for troubleshooting end-to-end
tests. tests.
# Summary # Summary
With full text search under our belt and a test to verify it, let's go to step 4 to learn how to With full text search under our belt and a test to verify it, let's go to step 4 to learn how to
add sorting capability to the phone app. add sorting capability to the phone app.
<ul doc:tutorial-nav="3"></ul> <ul doc:tutorial-nav="3"></ul>

View file

@ -2,38 +2,27 @@
@name Tutorial: 4 - Two-way Data Binding @name Tutorial: 4 - Two-way Data Binding
@description @description
<ul doc:tutorial-nav="4"></ul> <ul doc:tutorial-nav="4"></ul>
In this step, you will add a feature to let your users control the order of the items in the phone In this step, you will add a feature to let your users control the order of the items in the phone
list. The dynamic ordering is implemented by creating a new model property, wiring it together with list. The dynamic ordering is implemented by creating a new model property, wiring it together with
the repeater, and letting the data binding magic do the rest of the work. the repeater, and letting the data binding magic do the rest of the work.
<doc:tutorial-instructions step="4"></doc:tutorial-instructions> <doc:tutorial-instructions step="4"></doc:tutorial-instructions>
You should see that in addition to the search box, the app displays a drop down menu that allows You should see that in addition to the search box, the app displays a drop down menu that allows
users to control the order in which the phones are listed. users to control the order in which the phones are listed.
The most important changes are listed below. You can see the full diff on {@link The most important changes are listed below. You can see the full diff on {@link
https://github.com/angular/angular-phonecat/compare/step-3...step-4 https://github.com/angular/angular-phonecat/compare/step-3...step-4
GitHub}: GitHub}:
## Template ## Template
__`app/index.html`:__ __`app/index.html`:__
<pre> <pre>
... ...
@ -50,7 +39,6 @@ __`app/index.html`:__
</li> </li>
</ul> </ul>
<ul class="phones"> <ul class="phones">
<li ng:repeat="phone in phones.$filter(query).$orderBy(orderProp)"> <li ng:repeat="phone in phones.$filter(query).$orderBy(orderProp)">
{{phone.name}} {{phone.name}}
@ -60,26 +48,20 @@ __`app/index.html`:__
... ...
</pre> </pre>
In the `index.html` template we made the following changes: In the `index.html` template we made the following changes:
* First, we added a `<select>` html element named `orderProp`, so that our users can pick from the * First, we added a `<select>` html element named `orderProp`, so that our users can pick from the
two provided sorting options. two provided sorting options.
<img src="img/tutorial/tutorial_04-06_final.png"> <img src="img/tutorial/tutorial_04-06_final.png">
* We then chained the `$filter` method with {@link api/angular.Array.orderBy `$orderBy`} method to * We then chained the `$filter` method with {@link api/angular.Array.orderBy `$orderBy`} method to
further process the input into the repeater. `$orderBy` is a utility method similar to {@link further process the input into the repeater. `$orderBy` is a utility method similar to {@link
api/angular.Array.filter `$filter`}, but instead of filtering an array, it reorders it. api/angular.Array.filter `$filter`}, but instead of filtering an array, it reorders it.
Angular creates a two way data-binding between the select element and the `orderProp` model. Angular creates a two way data-binding between the select element and the `orderProp` model.
`orderProp` is then used as the input for the `$orderBy` method. `orderProp` is then used as the input for the `$orderBy` method.
As we discussed in the section about data-binding and the repeater in step 3, whenever the model As we discussed in the section about data-binding and the repeater in step 3, whenever the model
changes (for example because a user changes the order with the select drop down menu), angular's changes (for example because a user changes the order with the select drop down menu), angular's
data-binding will cause the view to automatically update. No bloated DOM manipulation code is data-binding will cause the view to automatically update. No bloated DOM manipulation code is
@ -87,17 +69,12 @@ necessary!
## Controller ## Controller
__`app/js/controller.js`:__ __`app/js/controller.js`:__
<pre> <pre>
/* App Controllers */ /* App Controllers */
function PhoneListCtrl() { function PhoneListCtrl() {
this.phones = [{"name": "Nexus S", this.phones = [{"name": "Nexus S",
"snippet": "Fast just got faster with Nexus S.", "snippet": "Fast just got faster with Nexus S.",
@ -109,21 +86,17 @@ function PhoneListCtrl() {
"snippet": "The Next, Next Generation tablet.", "snippet": "The Next, Next Generation tablet.",
"age": 2}]; "age": 2}];
this.orderProp = 'age'; this.orderProp = 'age';
} }
</pre> </pre>
* We modified the `phones` model - the array of phones - and added an `age` property to each phone * We modified the `phones` model - the array of phones - and added an `age` property to each phone
record. This property is used to order phones by age. record. This property is used to order phones by age.
* We added a line to the controller that sets the default value of `orderProp` to `age`. If we had * We added a line to the controller that sets the default value of `orderProp` to `age`. If we had
not set the default value here, angular would have used the value of the first `<option>` element not set the default value here, angular would have used the value of the first `<option>` element
(`'name'`) when it initialized the data model. (`'name'`) when it initialized the data model.
This is a good time to talk about two-way data-binding. Notice that when the app is loaded in the This is a good time to talk about two-way data-binding. Notice that when the app is loaded in the
browser, "Newest" is selected in the drop down menu. This is because we set `orderProp` to `'age'` browser, "Newest" is selected in the drop down menu. This is because we set `orderProp` to `'age'`
in the controller. So the binding works in the direction from our model to the UI. Now if you in the controller. So the binding works in the direction from our model to the UI. Now if you
@ -133,39 +106,28 @@ to the model.
## Test ## Test
The changes we made should be verified with both a unit test and an end-to-end test. Let's look at The changes we made should be verified with both a unit test and an end-to-end test. Let's look at
the unit test first. the unit test first.
__`test/unit/controllerSpec.js`:__ __`test/unit/controllerSpec.js`:__
<pre> <pre>
describe('PhoneCat controllers', function() { describe('PhoneCat controllers', function() {
describe('PhoneListCtrl', function(){ describe('PhoneListCtrl', function(){
var scope, $browser, ctrl; var scope, $browser, ctrl;
beforeEach(function() { beforeEach(function() {
ctrl = new PhoneListCtrl(); ctrl = new PhoneListCtrl();
}); });
it('should create "phones" model with 3 phones', function() { it('should create "phones" model with 3 phones', function() {
expect(ctrl.phones.length).toBe(3); expect(ctrl.phones.length).toBe(3);
}); });
it('should set the default value of orderProp model', function() { it('should set the default value of orderProp model', function() {
expect(ctrl.orderProp).toBe('age'); expect(ctrl.orderProp).toBe('age');
}); });
@ -174,49 +136,37 @@ describe('PhoneCat controllers', function() {
</pre> </pre>
The unit test now verifies that the default ordering property is set. The unit test now verifies that the default ordering property is set.
We used Jasmine's API to extract the controller construction into a `beforeEach` block, which is We used Jasmine's API to extract the controller construction into a `beforeEach` block, which is
shared by all tests in the parent `describe` block. shared by all tests in the parent `describe` block.
To run the unit tests, once again execute the `./scripts/test.sh` script and you should see the To run the unit tests, once again execute the `./scripts/test.sh` script and you should see the
following output. following output.
Chrome: Runner reset. Chrome: Runner reset.
.. ..
Total 2 tests (Passed: 2; Fails: 0; Errors: 0) (3.00 ms) Total 2 tests (Passed: 2; Fails: 0; Errors: 0) (3.00 ms)
Chrome 11.0.696.57 Mac OS: Run 2 tests (Passed: 2; Fails: 0; Errors 0) (3.00 ms) Chrome 11.0.696.57 Mac OS: Run 2 tests (Passed: 2; Fails: 0; Errors 0) (3.00 ms)
Let's turn our attention to the end-to-end test. Let's turn our attention to the end-to-end test.
__`test/e2e/scenarios.js`:__ __`test/e2e/scenarios.js`:__
<pre> <pre>
... ...
it('should be possible to control phone order via the drop down select box', it('should be possible to control phone order via the drop down select box',
function() { function() {
// narrow the dataset to make the test assertions shorter // narrow the dataset to make the test assertions shorter
input('query').enter('tablet'); input('query').enter('tablet');
expect(repeater('.phones li', 'Phone List').column('a')). expect(repeater('.phones li', 'Phone List').column('a')).
toEqual(["Motorola XOOM\u2122 with Wi-Fi", toEqual(["Motorola XOOM\u2122 with Wi-Fi",
"MOTOROLA XOOM\u2122"]); "MOTOROLA XOOM\u2122"]);
select('orderProp').option('alphabetical'); select('orderProp').option('alphabetical');
expect(repeater('.phones li', 'Phone List').column('a')). expect(repeater('.phones li', 'Phone List').column('a')).
toEqual(["MOTOROLA XOOM\u2122", toEqual(["MOTOROLA XOOM\u2122",
"Motorola XOOM\u2122 with Wi-Fi"]); "Motorola XOOM\u2122 with Wi-Fi"]);
@ -224,36 +174,26 @@ __`test/e2e/scenarios.js`:__
... ...
</pre> </pre>
The end-to-end test verifies that the ordering mechanism of the select box is working correctly. The end-to-end test verifies that the ordering mechanism of the select box is working correctly.
You can now refresh the browser tab with the end-to-end test runner to see the tests run, or you You can now refresh the browser tab with the end-to-end test runner to see the tests run, or you
can see them running on {@link can see them running on {@link
http://angular.github.com/angular-phonecat/step-4/test/e2e/runner.html http://angular.github.com/angular-phonecat/step-4/test/e2e/runner.html
angular's server}. angular's server}.
# Experiments # Experiments
* In the `PhoneListCtrl` controller, remove the statement that sets the `orderProp` value and * In the `PhoneListCtrl` controller, remove the statement that sets the `orderProp` value and
you'll see that the ordering as well as the current selection in the dropdown menu will default to you'll see that the ordering as well as the current selection in the dropdown menu will default to
"Alphabetical". "Alphabetical".
* Add an `{{orderProp}}` binding into the `index.html` template to display its current value as * Add an `{{orderProp}}` binding into the `index.html` template to display its current value as
text. text.
# Summary # Summary
Now that you have added list sorting and tested the app, go to step 5 to learn about angular Now that you have added list sorting and tested the app, go to step 5 to learn about angular
services and how angular uses dependency injection. services and how angular uses dependency injection.
<ul doc:tutorial-nav="4"></ul> <ul doc:tutorial-nav="4"></ul>

View file

@ -2,40 +2,29 @@
@name Tutorial: 5 - XHRs & Dependency Injection @name Tutorial: 5 - XHRs & Dependency Injection
@description @description
<ul doc:tutorial-nav="5"></ul> <ul doc:tutorial-nav="5"></ul>
Enough of building an app with three phones in a hard-coded dataset! Let's fetch a larger dataset Enough of building an app with three phones in a hard-coded dataset! Let's fetch a larger dataset
from our server using one of angular's built-in {@link api/angular.service services} called {@link from our server using one of angular's built-in {@link api/angular.service services} called {@link
api/angular.service.$xhr $xhr}. We will use angular's {@link guide/dev_guide.di dependency api/angular.service.$xhr $xhr}. We will use angular's {@link guide/dev_guide.di dependency
injection (DI)} to provide the service to the `PhoneListCtrl` controller. injection (DI)} to provide the service to the `PhoneListCtrl` controller.
<doc:tutorial-instructions step="5"></doc:tutorial-instructions> <doc:tutorial-instructions step="5"></doc:tutorial-instructions>
You should now see a list of 20 phones. You should now see a list of 20 phones.
The most important changes are listed below. You can see the full diff on {@link The most important changes are listed below. You can see the full diff on {@link
https://github.com/angular/angular-phonecat/compare/step-4...step-5 https://github.com/angular/angular-phonecat/compare/step-4...step-5
GitHub}: GitHub}:
## Data ## Data
The `app/phones/phone.json` file in your project is a dataset that contains a larger list of phones The `app/phones/phone.json` file in your project is a dataset that contains a larger list of phones
stored in the JSON format. stored in the JSON format.
Following is a sample of the file: Following is a sample of the file:
<pre> <pre>
[ [
@ -51,125 +40,96 @@ Following is a sample of the file:
</pre> </pre>
## Controller ## Controller
We'll use angular's {@link api/angular.service.$xhr $xhr} service in our controller to make an HTTP We'll use angular's {@link api/angular.service.$xhr $xhr} service in our controller to make an HTTP
request to your web server to fetch the data in the `app/phones/phones.json` file. `$xhr` is just request to your web server to fetch the data in the `app/phones/phones.json` file. `$xhr` is just
one of several built-in {@link api/angular.service angular services} that handle common operations one of several built-in {@link api/angular.service angular services} that handle common operations
in web apps. Angular injects these services for you where you need them. in web apps. Angular injects these services for you where you need them.
Services are managed by angular's {@link guide/dev_guide.di DI subsystem}. Dependency injection Services are managed by angular's {@link guide/dev_guide.di DI subsystem}. Dependency injection
helps to make your web apps both well-structured (e.g., separate components for presentation, data, helps to make your web apps both well-structured (e.g., separate components for presentation, data,
and control) and loosely coupled (dependencies between components are not resolved by the and control) and loosely coupled (dependencies between components are not resolved by the
components themselves, but by the DI subsystem). components themselves, but by the DI subsystem).
__`app/js/controllers.js:`__ __`app/js/controllers.js:`__
<pre> <pre>
function PhoneListCtrl($xhr) { function PhoneListCtrl($xhr) {
var self = this; var self = this;
$xhr('GET', 'phones/phones.json', function(code, response) { $xhr('GET', 'phones/phones.json', function(code, response) {
self.phones = response; self.phones = response;
}); });
self.orderProp = 'age'; self.orderProp = 'age';
} }
//PhoneListCtrl.$inject = ['$xhr']; //PhoneListCtrl.$inject = ['$xhr'];
</pre> </pre>
`$xhr` makes an HTTP GET request to our web server, asking for `phone/phones.json` (the url is `$xhr` makes an HTTP GET request to our web server, asking for `phone/phones.json` (the url is
relative to our `index.html` file). The server responds by providing the data in the json file. relative to our `index.html` file). The server responds by providing the data in the json file.
(The response might just as well have been dynamically generated by a backend server. To the (The response might just as well have been dynamically generated by a backend server. To the
browser and our app they both look the same. For the sake of simplicity we used a json file in this browser and our app they both look the same. For the sake of simplicity we used a json file in this
tutorial.) tutorial.)
The `$xhr` service takes a callback as the last argument. This callback is used to process the The `$xhr` service takes a callback as the last argument. This callback is used to process the
response. We assign the response to the scope controlled by the controller, as a model called response. We assign the response to the scope controlled by the controller, as a model called
`phones`. Notice that angular detected the json response and parsed it for us! `phones`. Notice that angular detected the json response and parsed it for us!
To use a service in angular, you simply declare the names of the services you need as arguments to To use a service in angular, you simply declare the names of the services you need as arguments to
the controller's constructor function, as follows: the controller's constructor function, as follows:
function PhoneListCtrl($xhr) {...} function PhoneListCtrl($xhr) {...}
Angular's dependency injector provides services to your controller when the controller is being Angular's dependency injector provides services to your controller when the controller is being
constructed. The dependency injector also takes care of creating any transitive dependencies the constructed. The dependency injector also takes care of creating any transitive dependencies the
service may have (services often depend upon other services). service may have (services often depend upon other services).
<img src="img/tutorial/xhr_service_final.png"> <img src="img/tutorial/xhr_service_final.png">
### '$' Prefix Naming Convention ### '$' Prefix Naming Convention
You can create your own services, and in fact we will do exactly that in step 11. As a naming You can create your own services, and in fact we will do exactly that in step 11. As a naming
convention, angular's built-in services, Scope methods and a few other angular APIs have a '$' convention, angular's built-in services, Scope methods and a few other angular APIs have a '$'
prefix in front of the name. Don't use a '$' prefix when naming your services and models, in order prefix in front of the name. Don't use a '$' prefix when naming your services and models, in order
to avoid any possible naming collisions. to avoid any possible naming collisions.
### A Note on Minification ### A Note on Minification
Since angular infers the controller's dependencies from the names of arguments to the controller's Since angular infers the controller's dependencies from the names of arguments to the controller's
constructor function, if you were to {@link http://en.wikipedia.org/wiki/Minification_(programming) constructor function, if you were to {@link http://en.wikipedia.org/wiki/Minification_(programming)
minify} the JavaScript code for `PhoneListCtrl` controller, all of its function arguments would be minify} the JavaScript code for `PhoneListCtrl` controller, all of its function arguments would be
minified as well, and the dependency injector would not being able to identify services correctly. minified as well, and the dependency injector would not being able to identify services correctly.
To overcome issues caused by minification, just assign an array with service identifier strings To overcome issues caused by minification, just assign an array with service identifier strings
into the `$inject` property of the controller function, just like the last line in the snippet into the `$inject` property of the controller function, just like the last line in the snippet
(commented out) suggests: (commented out) suggests:
PhoneListCtrl.$inject = ['$xhr']; PhoneListCtrl.$inject = ['$xhr'];
## Test ## Test
__`test/unit/controllersSpec.js`:__ __`test/unit/controllersSpec.js`:__
Because we started using dependency injection and our controller has dependencies, constructing the Because we started using dependency injection and our controller has dependencies, constructing the
controller in our tests is a bit more complicated. We could use the `new` operator and provide the controller in our tests is a bit more complicated. We could use the `new` operator and provide the
constructor with some kind of fake `$xhr` implementation. However, the recommended (and easier) way constructor with some kind of fake `$xhr` implementation. However, the recommended (and easier) way
is to create a controller in the test environment in the same way that angular does it in the is to create a controller in the test environment in the same way that angular does it in the
production code behind the scenes, as follows: production code behind the scenes, as follows:
<pre> <pre>
describe('PhoneCat controllers', function() { describe('PhoneCat controllers', function() {
describe('PhoneListCtrl', function() { describe('PhoneListCtrl', function() {
var scope, $browser, ctrl; var scope, $browser, ctrl;
beforeEach(function() { beforeEach(function() {
scope = angular.scope(); scope = angular.scope();
$browser = scope.$service('$browser'); $browser = scope.$service('$browser');
$browser.xhr.expectGET('phones/phones.json') $browser.xhr.expectGET('phones/phones.json')
.respond([{name: 'Nexus S'}, .respond([{name: 'Nexus S'},
{name: 'Motorola DROID'}]); {name: 'Motorola DROID'}]);
@ -178,59 +138,46 @@ describe('PhoneCat controllers', function() {
}); });
</pre> </pre>
We created the controller in the test environment, as follows: We created the controller in the test environment, as follows:
* We created a root scope object by calling `angular.scope()` * We created a root scope object by calling `angular.scope()`
* We called `scope.$new(PhoneListCtrl)` to get angular to create the child scope associated with * We called `scope.$new(PhoneListCtrl)` to get angular to create the child scope associated with
the `PhoneListCtrl` controller the `PhoneListCtrl` controller
Because our code now uses the `$xhr` service to fetch the phone list data in our controller, before Because our code now uses the `$xhr` service to fetch the phone list data in our controller, before
we create the `PhoneListCtrl` child scope, we need to tell the testing harness to expect an we create the `PhoneListCtrl` child scope, we need to tell the testing harness to expect an
incoming request from the controller. To do this we: incoming request from the controller. To do this we:
* Use the {@link api/angular.scope.$service `$service`} method to retrieve the `$browser` service, * Use the {@link api/angular.scope.$service `$service`} method to retrieve the `$browser` service,
a service that angular uses to represent various browser APIs. In tests, angular automatically uses a service that angular uses to represent 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 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. native APIs and the global state associated with them.
* Use the `$browser.xhr.expectGET` method to train the `$browser` object to expect an incoming HTTP * Use the `$browser.xhr.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 request and tell it what to respond with. Note that the responses are not returned before we call
the `$browser.xhr.flush` method. the `$browser.xhr.flush` method.
Now, we will make assertions to verify that the `phones` model doesn't exist on the scope, before Now, we will make assertions to verify that the `phones` model doesn't exist on the scope, before
the response is received: the response is received:
<pre> <pre>
it('should create "phones" model with 2 phones fetched from xhr', function() { it('should create "phones" model with 2 phones fetched from xhr', function() {
expect(ctrl.phones).toBeUndefined(); expect(ctrl.phones).toBeUndefined();
$browser.xhr.flush(); $browser.xhr.flush();
expect(ctrl.phones).toEqual([{name: 'Nexus S'}, expect(ctrl.phones).toEqual([{name: 'Nexus S'},
{name: 'Motorola DROID'}]); {name: 'Motorola DROID'}]);
}); });
</pre> </pre>
* We flush the xhr queue in the browser by calling `$browser.xhr.flush()`. This causes the callback * 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. we passed into the `$xhr` service to be executed with the trained response.
* We make the assertions, verifying that the phone model now exists on the scope. * We make the assertions, verifying that the phone model now exists on the scope.
Finally, we verify that the default value of `orderProp` is set correctly: Finally, we verify that the default value of `orderProp` is set correctly:
<pre> <pre>
it('should set the default value of orderProp model', function() { it('should set the default value of orderProp model', function() {
expect(ctrl.orderProp).toBe('age'); expect(ctrl.orderProp).toBe('age');
@ -239,44 +186,31 @@ Finally, we verify that the default value of `orderProp` is set correctly:
}); });
</pre> </pre>
To run the unit tests, execute the `./scripts/test.sh` script and you should see the following To run the unit tests, execute the `./scripts/test.sh` script and you should see the following
output. output.
Chrome: Runner reset. Chrome: Runner reset.
.. ..
Total 2 tests (Passed: 2; Fails: 0; Errors: 0) (3.00 ms) Total 2 tests (Passed: 2; Fails: 0; Errors: 0) (3.00 ms)
Chrome 11.0.696.57 Mac OS: Run 2 tests (Passed: 2; Fails: 0; Errors 0) (3.00 ms) Chrome 11.0.696.57 Mac OS: Run 2 tests (Passed: 2; Fails: 0; Errors 0) (3.00 ms)
# Experiments # Experiments
* At the bottom of `index.html`, add a `{{phones}}` binding to see the list of phones displayed in * At the bottom of `index.html`, add a `{{phones}}` binding to see the list of phones displayed in
json format. json format.
* In the `PhoneListCtrl` controller, pre-process the xhr response by limiting the number of phones * In the `PhoneListCtrl` controller, pre-process the xhr response by limiting the number of phones
to the first 5 in the list. Use the following code in the xhr callback: to the first 5 in the list. Use the following code in the xhr callback:
self.phones = response.splice(0, 5); self.phones = response.splice(0, 5);
# Summary # Summary
Now that you have learned how easy it is to use angular services (thanks to angular's Now that you have learned how easy it is to use angular services (thanks to angular's
implementation of dependency injection), go to step 6, where you will add some thumbnail images of implementation of dependency injection), go to step 6, where you will add some thumbnail images of
phones and some links. phones and some links.
<ul doc:tutorial-nav="5"></ul> <ul doc:tutorial-nav="5"></ul>

View file

@ -2,41 +2,29 @@
@name Tutorial: 6 - Templating Links & Images @name Tutorial: 6 - Templating Links & Images
@description @description
<ul doc:tutorial-nav="6"></ul> <ul doc:tutorial-nav="6"></ul>
In this step, you will add thumbnail images for the phones in the phone list, and links that, for In this step, you will add thumbnail images for the phones in the phone list, and links that, for
now, will go nowhere. In subsequent steps you will use the links to display additional information now, will go nowhere. In subsequent steps you will use the links to display additional information
about the phones in the catalog. about the phones in the catalog.
<doc:tutorial-instructions step="6"></doc:tutorial-instructions> <doc:tutorial-instructions step="6"></doc:tutorial-instructions>
You should now see links and images of the phones in the list. You should now see links and images of the phones in the list.
The most important changes are listed below. You can see the full diff on {@link The most important changes are listed below. You can see the full diff on {@link
https://github.com/angular/angular-phonecat/compare/step-5...step-6 https://github.com/angular/angular-phonecat/compare/step-5...step-6
GitHub}: GitHub}:
## Data ## Data
Note that the `phones.json` file contains unique ids and image urls for each of the phones. The Note that the `phones.json` file contains unique ids and image urls for each of the phones. The
urls point to the `app/img/phones/` directory. urls point to the `app/img/phones/` directory.
__`app/phones/phones.json`__ (sample snippet): __`app/phones/phones.json`__ (sample snippet):
<pre> <pre>
[ [
@ -52,11 +40,8 @@ __`app/phones/phones.json`__ (sample snippet):
</pre> </pre>
## Template ## Template
__`app/index.html`:__ __`app/index.html`:__
<pre> <pre>
... ...
@ -70,13 +55,11 @@ __`app/index.html`:__
... ...
</pre> </pre>
To dynamically generate links that will in the future lead to phone detail pages, we used the To dynamically generate links that will in the future lead to phone detail pages, we used the
now-familiar {@link guide/dev_guide.compiler.markup double-curly brace markup} in the `href` now-familiar {@link guide/dev_guide.compiler.markup double-curly brace markup} in the `href`
attribute values. In step 2, we added the `{{phone.name}}` binding as the element content. In this attribute values. In step 2, we added the `{{phone.name}}` binding as the element content. In this
step the '{{phone.id}}' binding is used in the element attribute. step the '{{phone.id}}' binding is used in the element attribute.
We also added phone images next to each record using an image tag with the {@link We also added phone images next to each record using an image tag with the {@link
api/angular.directive.ng:src ng:src} directive. That directive prevents the browser from treating api/angular.directive.ng:src ng:src} directive. That directive prevents the browser from treating
the angular `{{ exppression }}` markup literally, which it would have done if we had only specified the angular `{{ exppression }}` markup literally, which it would have done if we had only specified
@ -84,11 +67,8 @@ an attribute binding in a regular `src` attribute (`<img src="{{phone.imageUrl}}
`ng:src` prevents the browser from making an http request to an invalid location. `ng:src` prevents the browser from making an http request to an invalid location.
## Test ## Test
__`test/e2e/scenarios.js`__: __`test/e2e/scenarios.js`__:
<pre> <pre>
... ...
@ -100,36 +80,26 @@ __`test/e2e/scenarios.js`__:
... ...
</pre> </pre>
We added a new end-to-end test to verify that the app is generating correct links to the phone We added a new end-to-end test to verify that the app is generating correct links to the phone
views that we will implement in the upcoming steps. views that we will implement in the upcoming steps.
You can now refresh the browser tab with the end-to-end test runner to see the tests run, or you You can now refresh the browser tab with the end-to-end test runner to see the tests run, or you
can see them running on {@link can see them running on {@link
http://angular.github.com/angular-phonecat/step-6/test/e2e/runner.html http://angular.github.com/angular-phonecat/step-6/test/e2e/runner.html
angular's server}. angular's server}.
# Experiments # Experiments
* Replace the `ng:src` directive with a plain old `<src>` attribute. Using tools such as Firebug, * Replace the `ng:src` directive with a plain old `<src>` attribute. Using tools such as Firebug,
or Chrome's Web Inspector, or inspecting the webserver access logs, confirm that the app is indeed or Chrome's Web Inspector, or inspecting the webserver access logs, confirm that the app is indeed
making an extraneous request to `/app/%7B%7Bphone.imageUrl%7D%7D` (or making an extraneous request to `/app/%7B%7Bphone.imageUrl%7D%7D` (or
`/app/index.html/{{phone.imageUrl}}`). `/app/index.html/{{phone.imageUrl}}`).
# Summary # Summary
Now that you have added phone images and links, go to step 7 to learn about angular layout Now that you have added phone images and links, go to step 7 to learn about angular layout
templates and how angular makes it easy to create applications that have multiple views. templates and how angular makes it easy to create applications that have multiple views.
<ul doc:tutorial-nav="6"></ul> <ul doc:tutorial-nav="6"></ul>

View file

@ -2,52 +2,38 @@
@name Tutorial: 7 - Routing & Multiple Views @name Tutorial: 7 - Routing & Multiple Views
@description @description
<ul doc:tutorial-nav="7"></ul> <ul doc:tutorial-nav="7"></ul>
In this step, you will learn how to create a layout template and how to build an app that has In this step, you will learn how to create a layout template and how to build an app that has
multiple views by adding routing. multiple views by adding routing.
<doc:tutorial-instructions step="7"></doc:tutorial-instructions> <doc:tutorial-instructions step="7"></doc:tutorial-instructions>
Note that you are redirected to `app/index.html#/phones` and the same phone list appears in the Note that you are redirected to `app/index.html#/phones` and the same phone list appears in the
browser. When you click on a phone link the stub of a phone detail page is displayed. browser. When you click on a phone link the stub of a phone detail page is displayed.
The most important changes are listed below. You can see the full diff on {@link The most important changes are listed below. You can see the full diff on {@link
https://github.com/angular/angular-phonecat/compare/step-6...step-7 https://github.com/angular/angular-phonecat/compare/step-6...step-7
GitHub}: GitHub}:
## Multiple Views, Routing and Layout Template ## Multiple Views, Routing and Layout Template
Our app is slowly growing and becoming more complex. Before step 7, the app provided our users with Our app is slowly growing and becoming more complex. Before step 7, the app provided our users with
a single view (the list of all phones), and all of the template code was located in the a single view (the list of all phones), and all of the template code was located in the
`index.html` file. The next step in building the app is to add a view that will show detailed `index.html` file. The next step in building the app is to add a view that will show detailed
information about each of the devices in our list. 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 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` 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 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 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. the current "route" — the view that is currently displayed to the user.
Application routes in angular are declared via the {@link api/angular.service.$route $route} Application routes in angular are declared via the {@link api/angular.service.$route $route}
service. This service makes it easy to wire together controllers, view templates, and the current service. This 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 URL location in the browser. Using this feature we can implement {@link
@ -55,106 +41,83 @@ http://en.wikipedia.org/wiki/Deep_linking deep linking}, which lets us utilize t
history (back and forward navigation) and bookmarks. history (back and forward navigation) and bookmarks.
## Controllers ## Controllers
__`app/js/controller.js`:__ __`app/js/controller.js`:__
<pre> <pre>
function PhoneCatCtrl($route) { function PhoneCatCtrl($route) {
var self = this; var self = this;
$route.when('/phones', $route.when('/phones',
{template: 'partials/phone-list.html', controller: PhoneListCtrl}); {template: 'partials/phone-list.html', controller: PhoneListCtrl});
$route.when('/phones/:phoneId', $route.when('/phones/:phoneId',
{template: 'partials/phone-detail.html', controller: PhoneDetailCtrl}); {template: 'partials/phone-detail.html', controller: PhoneDetailCtrl});
$route.otherwise({redirectTo: '/phones'}); $route.otherwise({redirectTo: '/phones'});
$route.onChange(function() { $route.onChange(function() {
self.params = $route.current.params; self.params = $route.current.params;
}); });
$route.parent(this); $route.parent(this);
} }
//PhoneCatCtrl.$inject = ['$route']; //PhoneCatCtrl.$inject = ['$route'];
... ...
</pre> </pre>
We created a new controller called `PhoneCatCtrl`. We declared its dependency on the `$route` We created a new controller called `PhoneCatCtrl`. We declared its dependency on the `$route`
service and used this service to declare that our application consists of two different views: service and used this service to declare that our application consists of two different views:
* The phone list view will be shown when the URL hash fragment is `/phone`. To construct this view, * The phone list view will be shown when the URL hash fragment is `/phone`. To construct this view,
angular will use the `phone-list.html` template and the `PhoneListCtrl` controller. angular will use the `phone-list.html` template and the `PhoneListCtrl` controller.
* The phone details view will be shown when the URL hash fragment matches '/phone/:phoneId', where * The phone details view will be shown when the URL hash fragment matches '/phone/:phoneId', where
`:phoneId` is a variable part of the URL. To construct the phone details view, angular will use the `:phoneId` is a variable part of the URL. To construct the phone details view, angular will use the
`phone-detail.html` template and the `PhoneDetailCtrl` controller. `phone-detail.html` template and the `PhoneDetailCtrl` controller.
We reused the `PhoneListCtrl` controller that we constructed in previous steps and we added a new, We reused the `PhoneListCtrl` controller that we constructed in previous steps and we added a new,
empty `PhoneDetailCtrl` controller to the `app/js/controllers.js` file for the phone details view. empty `PhoneDetailCtrl` controller to the `app/js/controllers.js` file for the phone details view.
The statement `$route.otherwise({redirectTo: '/phones'})` triggers a redirection to `/phones` when The statement `$route.otherwise({redirectTo: '/phones'})` triggers a redirection to `/phones` when
the browser address doesn't match either of our routes. the browser address doesn't match either of our routes.
Thanks to the `$route.parent(this);` statement and `ng:controller="PhoneCatCtrl"` declaration in Thanks to the `$route.parent(this);` statement and `ng:controller="PhoneCatCtrl"` declaration in
the `index.html` template, the `PhoneCatCtrl` controller has a special role in our app. It is the the `index.html` template, the `PhoneCatCtrl` controller has a special role in our app. It is the
"root" controller and the parent controller for the other two sub-controllers (`PhoneListCtrl` and "root" controller and the parent controller for the other two sub-controllers (`PhoneListCtrl` and
`PhoneDetailCtrl`). The sub-controllers inherit the model properties and behavior from the root `PhoneDetailCtrl`). The sub-controllers inherit the model properties and behavior from the root
controller. controller.
Note the use of the `:phoneId` parameter in the second route declaration. The `$route` service uses Note the use of the `:phoneId` parameter in the second route declaration. The `$route` service uses
the route declaration — `'/phones/:phoneId'` — as a template that is matched against the current the route declaration — `'/phones/:phoneId'` — as a template that is matched against the current
URL. All variables defined with the `:` notation are extracted into the `$route.current.params` map. URL. All variables defined with the `:` notation are extracted into the `$route.current.params` map.
The `params` alias created in the {@link api/angular.service.$route `$route.onChange`} callback The `params` alias created in the {@link api/angular.service.$route `$route.onChange`} callback
allows us to use the `phoneId` property of this map in the `phone-details.html` template. allows us to use the `phoneId` property of this map in the `phone-details.html` template.
## Template ## Template
The `$route` service is usually used in conjunction with the {@link api/angular.widget.ng:view The `$route` service is usually used in conjunction with the {@link api/angular.widget.ng:view
ng:view} widget. The role of the `ng:view` widget is to include the view template for the current 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. route into the layout template, which makes it a perfect fit for our `index.html` template.
__`app/index.html`:__ __`app/index.html`:__
<pre> <pre>
... ...
<body ng:controller="PhoneCatCtrl"> <body ng:controller="PhoneCatCtrl">
<ng:view></ng:view> <ng:view></ng:view>
<script src="lib/angular/angular.js" ng:autobind></script> <script src="lib/angular/angular.js" ng:autobind></script>
<script src="js/controllers.js"></script> <script src="js/controllers.js"></script>
</body> </body>
</html> </html>
</pre> </pre>
Note that we removed most of the code in the `index.html` template and replaced it with a single Note that we removed most of the code in the `index.html` template and replaced it with a single
line containing the `ng:view` tag. The code that we removed was placed into the `phone-list.html` line containing the `ng:view` tag. The code that we removed was placed into the `phone-list.html`
template: template:
__`app/partials/phone-list.html`:__ __`app/partials/phone-list.html`:__
<pre> <pre>
<ul class="predicates"> <ul class="predicates">
@ -170,7 +133,6 @@ __`app/partials/phone-list.html`:__
</li> </li>
</ul> </ul>
<ul class="phones"> <ul class="phones">
<li ng:repeat="phone in phones.$filter(query).$orderBy(orderProp)"> <li ng:repeat="phone in phones.$filter(query).$orderBy(orderProp)">
<a href="#/phones/{{phone.id}}">{{phone.name}}</a> <a href="#/phones/{{phone.id}}">{{phone.name}}</a>
@ -180,31 +142,23 @@ __`app/partials/phone-list.html`:__
</ul> </ul>
</pre> </pre>
<img src="img/tutorial/tutorial_07_final.png"> <img src="img/tutorial/tutorial_07_final.png">
We also added a placeholder template for the phone details view: We also added a placeholder template for the phone details view:
__`app/partials/phone-list.html`:__ __`app/partials/phone-list.html`:__
<pre> <pre>
TBD: detail view for {{params.phoneId}} TBD: detail view for {{params.phoneId}}
</pre> </pre>
Note how we are using `params` model defined in the `PhoneCanCtrl` controller. Note how we are using `params` model defined in the `PhoneCanCtrl` controller.
## Test ## Test
To automatically verify that everything is wired properly, we wrote end-to-end tests that navigate To automatically verify that everything is wired properly, we wrote end-to-end tests that navigate
to various URLs and verify that the correct view was rendered. to various URLs and verify that the correct view was rendered.
<pre> <pre>
... ...
it('should redirect index.html to index.html#/phones', function() { it('should redirect index.html to index.html#/phones', function() {
@ -213,17 +167,13 @@ to various URLs and verify that the correct view was rendered.
}); });
... ...
describe('Phone detail view', function() { describe('Phone detail view', function() {
beforeEach(function() { beforeEach(function() {
browser().navigateTo('../../app/index.html#/phones/nexus-s'); browser().navigateTo('../../app/index.html#/phones/nexus-s');
}); });
it('should display placeholder page with phoneId', function() { it('should display placeholder page with phoneId', function() {
expect(binding('params.phoneId')).toBe('nexus-s'); expect(binding('params.phoneId')).toBe('nexus-s');
}); });
@ -231,40 +181,29 @@ to various URLs and verify that the correct view was rendered.
</pre> </pre>
You can now refresh the browser tab with the end-to-end test runner to see the tests run, or you You can now refresh the browser tab with the end-to-end test runner to see the tests run, or you
can see them running on {@link can see them running on {@link
http://angular.github.com/angular-phonecat/step-7/test/e2e/runner.html http://angular.github.com/angular-phonecat/step-7/test/e2e/runner.html
angular's server}. angular's server}.
# Experiments # Experiments
* Try to add an `{{orderProp}}` binding to `index.html`, and you'll see that nothing happens even * Try to add an `{{orderProp}}` binding to `index.html`, and you'll see that nothing happens even
when you are in the phone list view. This is because the `orderProp` model is visible only in the when you are in the phone list view. This is because the `orderProp` model is visible only in the
scope managed by `PhoneListCtrl`, which is associated with the `<ng:view>` element. If you add the scope managed by `PhoneListCtrl`, which is associated with the `<ng:view>` element. If you add the
same binding into the `phone-list.html` template, the binding will work as expected. same binding into the `phone-list.html` template, the binding will work as expected.
* In `PhoneCatCtrl`, create a new model called "`hero`" with `this.hero = 'Zoro'`. In * In `PhoneCatCtrl`, create a new model called "`hero`" with `this.hero = 'Zoro'`. In
`PhoneListCtrl` let's shadow it with `this.hero = 'Batman'`, and in `PhoneDetailCtrl` we'll use `PhoneListCtrl` let's shadow it with `this.hero = 'Batman'`, and in `PhoneDetailCtrl` we'll use
`this.hero = "Captain Proton"`. Then add the `<p>hero = {{hero}}</p>` to all three of our templates `this.hero = "Captain Proton"`. Then add the `<p>hero = {{hero}}</p>` to all three of our templates
(`index.html`, `phone-list.html`, and `phone-detail.html`). Open the app and you'll see scope (`index.html`, `phone-list.html`, and `phone-detail.html`). Open the app and you'll see scope
inheritance and model property shadowing do some wonders. inheritance and model property shadowing do some wonders.
# Summary # Summary
With the routing set up and the phone list view implemented, we're ready to go to step 8 to With the routing set up and the phone list view implemented, we're ready to go to step 8 to
implement the phone details view. implement the phone details view.
<ul doc:tutorial-nav="7"></ul> <ul doc:tutorial-nav="7"></ul>

View file

@ -2,43 +2,31 @@
@name Tutorial: 8 - More Templating @name Tutorial: 8 - More Templating
@description @description
<ul doc:tutorial-nav="8"></ul> <ul doc:tutorial-nav="8"></ul>
In this step, you will implement the phone details view, which is displayed when a user clicks on a In this step, you will implement the phone details view, which is displayed when a user clicks on a
phone in the phone list. phone in the phone list.
<doc:tutorial-instructions step="8"></doc:tutorial-instructions> <doc:tutorial-instructions step="8"></doc:tutorial-instructions>
Now when you click on a phone on the list, the phone details page with phone-specific information Now when you click on a phone on the list, the phone details page with phone-specific information
is displayed. is displayed.
To implement the phone details view we will use {@link api/angular.service.$xhr $xhr} to fetch our To implement the phone details view we will use {@link api/angular.service.$xhr $xhr} to fetch our
data, and we'll flesh out the `phone-details.html` view template. data, and we'll flesh out the `phone-details.html` view template.
The most important changes are listed below. You can see the full diff on {@link The most important changes are listed below. You can see the full diff on {@link
https://github.com/angular/angular-phonecat/compare/step-7...step-8 https://github.com/angular/angular-phonecat/compare/step-7...step-8
GitHub}: GitHub}:
## Data ## Data
In addition to `phones.json`, the `app/phones/` directory also contains one json file for each In addition to `phones.json`, the `app/phones/` directory also contains one json file for each
phone: phone:
__`app/phones/nexus-s.json`:__ (sample snippet) __`app/phones/nexus-s.json`:__ (sample snippet)
<pre> <pre>
{ {
@ -62,71 +50,53 @@ __`app/phones/nexus-s.json`:__ (sample snippet)
</pre> </pre>
Each of these files describes various properties of the phone using the same data structure. We'll Each of these files describes various properties of the phone using the same data structure. We'll
show this data in the phone detail view. show this data in the phone detail view.
## Controller ## Controller
We'll expand the `PhoneDetailCtrl` by using the `$xhr` service to fetch the json files. This works We'll expand the `PhoneDetailCtrl` by using the `$xhr` service to fetch the json files. This works
the same way as the phone list controller. the same way as the phone list controller.
__`app/js/controller.js`:__ __`app/js/controller.js`:__
<pre> <pre>
function PhoneDetailCtrl($xhr) { function PhoneDetailCtrl($xhr) {
var self = this; var self = this;
$xhr('GET', 'phones/' + self.params.phoneId + '.json', function(code, response) { $xhr('GET', 'phones/' + self.params.phoneId + '.json', function(code, response) {
self.phone = response; self.phone = response;
}); });
} }
//PhoneDetailCtrl.$inject = ['$xhr']; //PhoneDetailCtrl.$inject = ['$xhr'];
</pre> </pre>
To construct the URL for the HTTP request, we use `params.phoneId` extracted from the current route To construct the URL for the HTTP request, we use `params.phoneId` extracted from the current route
in the `PhoneCatCtrl` controller. in the `PhoneCatCtrl` controller.
## Template ## Template
The TBD placeholder line has been replaced with lists and bindings that comprise the phone details. The TBD placeholder line has been replaced with lists and bindings that comprise the phone details.
Note where we use the angular `{{expression}}` markup and `ng:repeater`s to project phone data from Note where we use the angular `{{expression}}` markup and `ng:repeater`s to project phone data from
our model into the view. our model into the view.
__`app/partials/phone-details.html`:__ __`app/partials/phone-details.html`:__
<pre> <pre>
<img ng:src="{{phone.images[0]}}" class="phone"/> <img ng:src="{{phone.images[0]}}" class="phone"/>
<h1>{{phone.name}}</h1> <h1>{{phone.name}}</h1>
<p>{{phone.description}}</p> <p>{{phone.description}}</p>
<ul class="phone-thumbs"> <ul class="phone-thumbs">
<li ng:repeat="img in phone.images"> <li ng:repeat="img in phone.images">
<img ng:src="{{img}}"/> <img ng:src="{{img}}"/>
</li> </li>
</ul> </ul>
<ul class="specs"> <ul class="specs">
<li> <li>
<span>Availability and Networks</span> <span>Availability and Networks</span>
@ -143,17 +113,13 @@ __`app/partials/phone-details.html`:__
</ul> </ul>
</pre> </pre>
<img src="img/tutorial/tutorial_08-09_final.png"> <img src="img/tutorial/tutorial_08-09_final.png">
## Test ## Test
We wrote a new unit test that is similar to the one we wrote for the `PhoneListCtrl` controller in We wrote a new unit test that is similar to the one we wrote for the `PhoneListCtrl` controller in
step 5. step 5.
__`test/unit/controllerSpec.js`:__ __`test/unit/controllerSpec.js`:__
<pre> <pre>
... ...
@ -162,46 +128,36 @@ __`test/unit/controllerSpec.js`:__
$browser.xhr.expectGET('phones/xyz.json').respond({name:'phone xyz'}); $browser.xhr.expectGET('phones/xyz.json').respond({name:'phone xyz'});
ctrl = scope.$new(PhoneDetailCtrl); ctrl = scope.$new(PhoneDetailCtrl);
expect(ctrl.phone).toBeUndefined(); expect(ctrl.phone).toBeUndefined();
$browser.xhr.flush(); $browser.xhr.flush();
expect(ctrl.phone).toEqual({name:'phone xyz'}); expect(ctrl.phone).toEqual({name:'phone xyz'});
}); });
... ...
</pre> </pre>
To run the unit tests, execute the `./scripts/test.sh` script and you should see the following To run the unit tests, execute the `./scripts/test.sh` script and you should see the following
output. output.
Chrome: Runner reset. Chrome: Runner reset.
... ...
Total 3 tests (Passed: 3; Fails: 0; Errors: 0) (5.00 ms) Total 3 tests (Passed: 3; Fails: 0; Errors: 0) (5.00 ms)
Chrome 11.0.696.57 Mac OS: Run 3 tests (Passed: 3; Fails: 0; Errors 0) (5.00 ms) Chrome 11.0.696.57 Mac OS: Run 3 tests (Passed: 3; Fails: 0; Errors 0) (5.00 ms)
We also added a new end-to-end test that navigates to the Nexus S detail page and verifies that the We also added a new end-to-end test that navigates to the Nexus S detail page and verifies that the
heading on the page is "Nexus S". heading on the page is "Nexus S".
__`test/e2e/scenarios.js`:__ __`test/e2e/scenarios.js`:__
<pre> <pre>
... ...
describe('Phone detail view', function() { describe('Phone detail view', function() {
beforeEach(function() { beforeEach(function() {
browser().navigateTo('../../app/index.html#/phones/nexus-s'); browser().navigateTo('../../app/index.html#/phones/nexus-s');
}); });
it('should display nexus-s page', function() { it('should display nexus-s page', function() {
expect(binding('phone.name')).toBe('Nexus S'); expect(binding('phone.name')).toBe('Nexus S');
}); });
@ -210,33 +166,23 @@ __`test/e2e/scenarios.js`:__
</pre> </pre>
You can now refresh the browser tab with the end-to-end test runner to see the tests run, or you You can now refresh the browser tab with the end-to-end test runner to see the tests run, or you
can see them running on {@link can see them running on {@link
http://angular.github.com/angular-phonecat/step-8/test/e2e/runner.html http://angular.github.com/angular-phonecat/step-8/test/e2e/runner.html
angular's server}. angular's server}.
# Experiments # Experiments
* Using the {@link * Using the {@link
https://docs.google.com/document/d/11L8htLKrh6c92foV71ytYpiKkeKpM4_a5-9c3HywfIc/edit?hl=en&pli=1# https://docs.google.com/document/d/11L8htLKrh6c92foV71ytYpiKkeKpM4_a5-9c3HywfIc/edit?hl=en&pli=1#
end-to-end test runner API}, write a test that verifies that we display 4 thumbnail images on the end-to-end test runner API}, write a test that verifies that we display 4 thumbnail images on the
Nexus S details page. Nexus S details page.
# Summary # Summary
Now that the phone details view is in place, proceed to step 9 to learn how to write your own Now that the phone details view is in place, proceed to step 9 to learn how to write your own
custom display filter. custom display filter.
<ul doc:tutorial-nav="8"></ul> <ul doc:tutorial-nav="8"></ul>

View file

@ -2,44 +2,31 @@
@name Tutorial: 9 - Filters @name Tutorial: 9 - Filters
@description @description
<ul doc:tutorial-nav="9"></ul> <ul doc:tutorial-nav="9"></ul>
In this step you will learn how to create your own custom display filter. In this step you will learn how to create your own custom display filter.
<doc:tutorial-instructions step="9"></doc:tutorial-instructions> <doc:tutorial-instructions step="9"></doc:tutorial-instructions>
Navigate to one of the detail pages. Navigate to one of the detail pages.
In the previous step, the details page displayed either "true" or "false" to indicate whether In the previous step, the details page displayed either "true" or "false" to indicate whether
certain phone features were present or not. We have used a custom filter to convert those text certain phone features were present or not. We have used a custom filter to convert those text
strings into glyphs: ✓ for "true", and ✘ for "false". Let's see, what the filter code looks like. strings into glyphs: ✓ for "true", and ✘ for "false". Let's see, what the filter code looks like.
The most important changes are listed below. You can see the full diff on {@link The most important changes are listed below. You can see the full diff on {@link
https://github.com/angular/angular-phonecat/compare/step-8...step-9 https://github.com/angular/angular-phonecat/compare/step-8...step-9
GitHub}: GitHub}:
## Custom Filter ## Custom Filter
In order to create a new filter, simply register your custom filter function with the {@link In order to create a new filter, simply register your custom filter function with the {@link
api/angular.filter `angular.filter`} API. api/angular.filter `angular.filter`} API.
__`app/js/filters.js`:__ __`app/js/filters.js`:__
<pre> <pre>
angular.filter('checkmark', function(input) { angular.filter('checkmark', function(input) {
@ -47,21 +34,16 @@ angular.filter('checkmark', function(input) {
}); });
</pre> </pre>
The name of our filter is "checkmark". The `input` evaluates to either `true` or `false`, and we The name of our filter is "checkmark". The `input` evaluates to either `true` or `false`, and we
return one of two unicode characters we have chosen to represent true or false (`\u2713` and return one of two unicode characters we have chosen to represent true or false (`\u2713` and
`\u2718`). `\u2718`).
## Template ## Template
Since the filter code lives in the `app/js/filters.js` file, we need to include this file in our Since the filter code lives in the `app/js/filters.js` file, we need to include this file in our
layout template. layout template.
__`app/index.html`:__ __`app/index.html`:__
<pre> <pre>
... ...
@ -70,19 +52,14 @@ __`app/index.html`:__
... ...
</pre> </pre>
The syntax for using filters in angular templates is as follows: The syntax for using filters in angular templates is as follows:
{{ expression | filter }} {{ expression | filter }}
Let's employ the filter in the phone details template: Let's employ the filter in the phone details template:
__`app/partials/phone-detail.html`:__ __`app/partials/phone-detail.html`:__
<pre> <pre>
... ...
@ -96,19 +73,14 @@ __`app/partials/phone-detail.html`:__
</pre> </pre>
## Test ## Test
Filters, like any other component, should be tested and these tests are very easy to write. Filters, like any other component, should be tested and these tests are very easy to write.
__`test/unit/filtersSpec.js`:__ __`test/unit/filtersSpec.js`:__
<pre> <pre>
describe('checkmark filter', function() { describe('checkmark filter', function() {
it('should convert boolean values to unicode checkmark or cross', function() { it('should convert boolean values to unicode checkmark or cross', function() {
expect(angular.filter.checkmark(true)).toBe('\u2713'); expect(angular.filter.checkmark(true)).toBe('\u2713');
expect(angular.filter.checkmark(false)).toBe('\u2718'); expect(angular.filter.checkmark(false)).toBe('\u2718');

View file

@ -2,80 +2,59 @@
@name Tutorial: 10 - Event Handlers @name Tutorial: 10 - Event Handlers
@description @description
<ul doc:tutorial-nav="10"></ul> <ul doc:tutorial-nav="10"></ul>
In this step, you will add a clickable phone image swapper to the phone details page. In this step, you will add a clickable phone image swapper to the phone details page.
<doc:tutorial-instructions step="10"></doc:tutorial-instructions> <doc:tutorial-instructions step="10"></doc:tutorial-instructions>
The phone details view displays one large image of the current phone and several smaller thumbnail 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 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 at how we can do this with angular. clicking on the desired thumbnail image. Let's have a look at how we can do this with angular.
The most important changes are listed below. You can see the full diff on {@link The most important changes are listed below. You can see the full diff on {@link
https://github.com/angular/angular-phonecat/compare/step-9...step-10 https://github.com/angular/angular-phonecat/compare/step-9...step-10
GitHub}: GitHub}:
## Controller ## Controller
__`app/js/controllers.js`:__ __`app/js/controllers.js`:__
<pre> <pre>
... ...
function PhoneDetailCtrl($xhr) { function PhoneDetailCtrl($xhr) {
var self = this; var self = this;
$xhr('GET', 'phones/' + self.params.phoneId + '.json', function(code, response) { $xhr('GET', 'phones/' + self.params.phoneId + '.json', function(code, response) {
self.phone = response; self.phone = response;
self.mainImageUrl = response.images[0]; self.mainImageUrl = response.images[0];
}); });
self.setImage = function(imageUrl) { self.setImage = function(imageUrl) {
self.mainImageUrl = imageUrl; self.mainImageUrl = imageUrl;
} }
} }
//PhoneDetailCtrl.$inject = ['$xhr']; //PhoneDetailCtrl.$inject = ['$xhr'];
</pre> </pre>
In the `PhoneDetailCtrl` controller, we created the `mainImageUrl` model property and set its In the `PhoneDetailCtrl` controller, we created the `mainImageUrl` model property and set its
default value to the first phone image url. default value to the first phone image url.
We also created a `setImage` controller method to change the value of `mainImageUrl`. We also created a `setImage` controller method to change the value of `mainImageUrl`.
## Template ## Template
__`app/partials/phone-detail.html`:__ __`app/partials/phone-detail.html`:__
<pre> <pre>
<img ng:src="{{mainImageUrl}}" class="phone"/> <img ng:src="{{mainImageUrl}}" class="phone"/>
... ...
<ul class="phone-thumbs"> <ul class="phone-thumbs">
<li ng:repeat="img in phone.images"> <li ng:repeat="img in phone.images">
<img ng:src="{{img}}" ng:click="setImage(img)"> <img ng:src="{{img}}" ng:click="setImage(img)">
@ -84,53 +63,40 @@ __`app/partials/phone-detail.html`:__
... ...
</pre> </pre>
We bound the `ng:src` attribute of the large image to the `mainImageUrl` property. We bound the `ng:src` attribute of the large image to the `mainImageUrl` property.
We also registered an {@link api/angular.directive.ng:click `ng:click`} handler with thumbnail We also registered an {@link api/angular.directive.ng:click `ng:click`} handler with thumbnail
images. When a user clicks on one of the thumbnail images, the handler will use the `setImage` images. When a user clicks on one of the thumbnail images, the handler will use the `setImage`
controller method to change the value of the `mainImageUrl` property to the url of the thumbnail controller method to change the value of the `mainImageUrl` property to the url of the thumbnail
image. image.
<img src="img/tutorial/tutorial_10-11_final.png"> <img src="img/tutorial/tutorial_10-11_final.png">
## Test ## Test
To verify this new feature, we added two end-to-end tests. One verifies that the main image is set To verify this new feature, we added two end-to-end tests. One verifies that the main image is set
to the first phone image by default. The second test clicks on several thumbnail images and to the first phone image by default. The second test clicks on several thumbnail images and
verifies that the main image changed appropriately. verifies that the main image changed appropriately.
__`test/e2e/scenarios.js`:__ __`test/e2e/scenarios.js`:__
<pre> <pre>
... ...
describe('Phone detail view', function() { describe('Phone detail view', function() {
beforeEach(function() { beforeEach(function() {
browser().navigateTo('../../app/index.html#/phones/nexus-s'); browser().navigateTo('../../app/index.html#/phones/nexus-s');
}); });
it('should display the first phone 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'); 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() { it('should swap main image if a thumbnail image is clicked on', function() {
element('.phone-thumbs li:nth-child(3) img').click(); element('.phone-thumbs li:nth-child(3) img').click();
expect(element('img.phone').attr('src')).toBe('img/phones/nexus-s.2.jpg'); expect(element('img.phone').attr('src')).toBe('img/phones/nexus-s.2.jpg');
element('.phone-thumbs li:nth-child(1) img').click(); element('.phone-thumbs li:nth-child(1) img').click();
expect(element('img.phone').attr('src')).toBe('img/phones/nexus-s.0.jpg'); expect(element('img.phone').attr('src')).toBe('img/phones/nexus-s.0.jpg');
}); });
@ -138,51 +104,37 @@ __`test/e2e/scenarios.js`:__
}); });
</pre> </pre>
You can now refresh the browser tab with the end-to-end test runner to see the tests run, or you You can now refresh the browser tab with the end-to-end test runner to see the tests run, or you
can see them running on {@link can see them running on {@link
http://angular.github.com/angular-phonecat/step-8/test/e2e/runner.html http://angular.github.com/angular-phonecat/step-8/test/e2e/runner.html
angular's server}. angular's server}.
# Experiments # Experiments
* Let's add a new controller method to `PhoneCatCtrl`: * Let's add a new controller method to `PhoneCatCtrl`:
this.hello(name) = function(name) { this.hello(name) = function(name) {
alert('Hello ' + (name || 'world') + '!'); alert('Hello ' + (name || 'world') + '!');
} }
and add: and add:
<button ng:click="hello('Elmo')">Hello</button> <button ng:click="hello('Elmo')">Hello</button>
to the `index.html` template. to the `index.html` template.
The controller methods are inherited between controllers/scopes, so you can use the same snippet The controller methods are inherited between controllers/scopes, so you can use the same snippet
in the `phone-list.html` template as well. in the `phone-list.html` template as well.
* Move the `hello` method from `PhoneCatCtrl` to `PhoneListCtrl` and you'll see that the button * Move the `hello` method from `PhoneCatCtrl` to `PhoneListCtrl` and you'll see that the button
declared in `index.html` will stop working, while the one declared in the `phone-list.html` declared in `index.html` will stop working, while the one declared in the `phone-list.html`
template remains operational. template remains operational.
# Summary # Summary
With the phone image swapper in place, we're ready for step 11 (the last step!) to learn an even With the phone image swapper in place, we're ready for step 11 (the last step!) to learn an even
better way to fetch data. better way to fetch data.
<ul doc:tutorial-nav="10"></ul> <ul doc:tutorial-nav="10"></ul>

View file

@ -2,42 +2,30 @@
@name Tutorial: 11 - REST and Custom Services @name Tutorial: 11 - REST and Custom Services
@description @description
<ul doc:tutorial-nav="11"></ul> <ul doc:tutorial-nav="11"></ul>
In this step, you will improve the way our app fetches data. In this step, you will improve the way our app fetches data.
<doc:tutorial-instructions step="11"></doc:tutorial-instructions> <doc:tutorial-instructions step="11"></doc:tutorial-instructions>
The last improvement we will make to our app is to define a custom service that represents a {@link The last improvement we will make to our app is to define a custom service that represents a {@link
http://en.wikipedia.org/wiki/Representational_State_Transfer RESTful} client. Using this client we 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 can make xhr requests for data in an easier way, without having to deal with the lower-level {@link
api/angular.service.$xhr $xhr} API, HTTP methods and URLs. api/angular.service.$xhr $xhr} API, HTTP methods and URLs.
The most important changes are listed below. You can see the full diff on {@link The most important changes are listed below. You can see the full diff on {@link
https://github.com/angular/angular-phonecat/compare/step-10...step-11 https://github.com/angular/angular-phonecat/compare/step-10...step-11
GitHub}: GitHub}:
## Template ## Template
The custom service is defined in `app/js/services.js` so we need to include this file in our layout The custom service is defined in `app/js/services.js` so we need to include this file in our layout
template: template:
__`app/index.html`.__ __`app/index.html`.__
<pre> <pre>
... ...
@ -45,10 +33,8 @@ __`app/index.html`.__
... ...
</pre> </pre>
## Service ## Service
__`app/js/services.js`.__ __`app/js/services.js`.__
<pre> <pre>
angular.service('Phone', function($resource) { angular.service('Phone', function($resource) {
@ -58,36 +44,29 @@ __`app/js/services.js`.__
}); });
</pre> </pre>
We used the {@link api/angular.service} API to register a custom service. We passed in the name of We used the {@link api/angular.service} API to register a custom service. We passed in the name of
the service - 'Phone' - and a factory function. The factory function is similar to a controller's the service - 'Phone' - and a factory function. The factory function is similar to a controller's
constructor in that both can declare dependencies via function arguments. The Phone service constructor in that both can declare dependencies via function arguments. The Phone service
declared a dependency on the `$resource` service. declared a dependency on the `$resource` service.
The {@link api/angular.service.$resource `$resource`} service makes it easy to create a {@link The {@link api/angular.service.$resource `$resource`} service makes it easy to create a {@link
http://en.wikipedia.org/wiki/Representational_State_Transfer RESTful} client with just a few lines http://en.wikipedia.org/wiki/Representational_State_Transfer RESTful} client with just a few lines
of code. This client can then be used in our application, instead of the lower-level {@link of code. This client can then be used in our application, instead of the lower-level {@link
api/angular.service.$xhr $xhr} service. api/angular.service.$xhr $xhr} service.
## Controller ## Controller
We simplified our sub-controllers (`PhoneListCtrl` and `PhoneDetailCtrl`) by factoring out the We simplified our sub-controllers (`PhoneListCtrl` and `PhoneDetailCtrl`) by factoring out the
lower-level {@link api/angular.service.$xhr $xhr} service, replacing it with a new service called lower-level {@link api/angular.service.$xhr $xhr} service, replacing it with a new service called
`Phone`. Angular's {@link api/angular.service.$resource `$resource`} service is easier to use than `Phone`. Angular's {@link api/angular.service.$resource `$resource`} service is easier to use than
{@link api/angular.service.$xhr $xhr} for interacting with data sources exposed as RESTful {@link api/angular.service.$xhr $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. resources. It is also easier now to understand what the code in our controllers is doing.
__`app/js/controllers.js`.__ __`app/js/controllers.js`.__
<pre> <pre>
... ...
function PhoneListCtrl(Phone_) { function PhoneListCtrl(Phone_) {
this.orderProp = 'age'; this.orderProp = 'age';
this.phones = Phone_.query(); this.phones = Phone_.query();
@ -95,40 +74,30 @@ function PhoneListCtrl(Phone_) {
//PhoneListCtrl.$inject = ['Phone']; //PhoneListCtrl.$inject = ['Phone'];
function PhoneDetailCtrl(Phone_) { function PhoneDetailCtrl(Phone_) {
var self = this; var self = this;
self.phone = Phone_.get({phoneId: self.params.phoneId}, function(phone) { self.phone = Phone_.get({phoneId: self.params.phoneId}, function(phone) {
self.mainImageUrl = phone.images[0]; self.mainImageUrl = phone.images[0];
}); });
... ...
} }
//PhoneDetailCtrl.$inject = ['Phone']; //PhoneDetailCtrl.$inject = ['Phone'];
</pre> </pre>
Notice how in `PhoneListCtrl` we replaced: Notice how in `PhoneListCtrl` we replaced:
$xhr('GET', 'phones/phones.json', function(code, response) { $xhr('GET', 'phones/phones.json', function(code, response) {
self.phones = response; self.phones = response;
}); });
with: with:
this.phones = Phone_.query(); this.phones = Phone_.query();
This is a simple statement that we want to query for all phones. This is a simple statement that we want to query for all phones.
An important thing to notice in the code above is that we don't pass any callback functions when An important thing to notice in the code above is that we don't pass any callback functions when
invoking methods of our Phone service. Although it looks as if the result were returned invoking methods of our Phone service. Although it looks as if the result were returned
synchronously, that is not the case at all. What is returned synchronously is a "future" — an synchronously, that is not the case at all. What is returned synchronously is a "future" — an
@ -136,22 +105,17 @@ object, which will be filled with data when the xhr response returns. Because of
in angular, we can use this future and bind it to our template. Then, when the data arrives, the in angular, we can use this future and bind it to our template. Then, when the data arrives, the
view will automatically update. view will automatically update.
Sometimes, relying on the future object and data-binding alone is not sufficient to do everything Sometimes, relying on the future object and data-binding alone is not sufficient to do everything
we require, so in these cases, we can add a callback to process the server response. The we require, so in these cases, we can add a callback to process the server response. The
`PhoneDetailCtrl` controller illustrates this by setting the `mainImageUrl` in a callback. `PhoneDetailCtrl` controller illustrates this by setting the `mainImageUrl` in a callback.
## Test ## Test
We have modified our unit tests to verify that our new service is issuing HTTP requests and We have modified our unit tests to verify that our new service is issuing HTTP requests and
processing them as expected. The tests also check that our controllers are interacting with the processing them as expected. The tests also check that our controllers are interacting with the
service correctly. service correctly.
The {@link api/angular.service.$resource $resource} client augments the response object with The {@link api/angular.service.$resource $resource} client augments the response object with
methods for updating and deleting the resource. If we were to use the standard `toEqual` matcher, methods for updating and deleting the resource. If we were to use the standard `toEqual` matcher,
our tests would fail because the test values would not match the responses exactly. To solve the our tests would fail because the test values would not match the responses exactly. To solve the
@ -161,13 +125,10 @@ http://pivotal.github.com/jasmine/jsdoc/symbols/jasmine.Matchers.html Jasmine ma
ignores methods. ignores methods.
__`test/unit/controllersSpec.js`:__ __`test/unit/controllersSpec.js`:__
<pre> <pre>
describe('PhoneCat controllers', function() { describe('PhoneCat controllers', function() {
beforeEach(function(){ beforeEach(function(){
this.addMatchers({ this.addMatchers({
toEqualData: function(expected) { toEqualData: function(expected) {
@ -176,92 +137,71 @@ describe('PhoneCat controllers', function() {
}); });
}); });
describe('PhoneListCtrl', function() { describe('PhoneListCtrl', function() {
var scope, $browser, ctrl; var scope, $browser, ctrl;
beforeEach(function() { beforeEach(function() {
scope = angular.scope(); scope = angular.scope();
$browser = scope.$service('$browser'); $browser = scope.$service('$browser');
$browser.xhr.expectGET('phones/phones.json') $browser.xhr.expectGET('phones/phones.json')
.respond([{name: 'Nexus S'}, {name: 'Motorola DROID'}]); .respond([{name: 'Nexus S'}, {name: 'Motorola DROID'}]);
ctrl = scope.$new(PhoneListCtrl); ctrl = scope.$new(PhoneListCtrl);
}); });
it('should create "phones" model with 2 phones fetched from xhr', function() { it('should create "phones" model with 2 phones fetched from xhr', function() {
expect(ctrl.phones).toEqual([]); expect(ctrl.phones).toEqual([]);
$browser.xhr.flush(); $browser.xhr.flush();
expect(ctrl.phones).toEqualData([{name: 'Nexus S'}, expect(ctrl.phones).toEqualData([{name: 'Nexus S'},
{name: 'Motorola DROID'}]); {name: 'Motorola DROID'}]);
}); });
it('should set the default value of orderProp model', function() { it('should set the default value of orderProp model', function() {
expect(ctrl.orderProp).toBe('age'); expect(ctrl.orderProp).toBe('age');
}); });
}); });
describe('PhoneDetailCtrl', function() { describe('PhoneDetailCtrl', function() {
var scope, $browser, ctrl; var scope, $browser, ctrl;
beforeEach(function() { beforeEach(function() {
scope = angular.scope(); scope = angular.scope();
$browser = scope.$service('$browser'); $browser = scope.$service('$browser');
}); });
beforeEach(function() { beforeEach(function() {
scope = angular.scope(); scope = angular.scope();
$browser = scope.$service('$browser'); $browser = scope.$service('$browser');
}); });
it('should fetch phone detail', function() { it('should fetch phone detail', function() {
scope.params = {phoneId:'xyz'}; scope.params = {phoneId:'xyz'};
$browser.xhr.expectGET('phones/xyz.json').respond({name:'phone xyz'}); $browser.xhr.expectGET('phones/xyz.json').respond({name:'phone xyz'});
ctrl = scope.$new(PhoneDetailCtrl); ctrl = scope.$new(PhoneDetailCtrl);
expect(ctrl.phone).toEqualData({}); expect(ctrl.phone).toEqualData({});
$browser.xhr.flush(); $browser.xhr.flush();
expect(ctrl.phone).toEqualData({name:'phone xyz'}); expect(ctrl.phone).toEqualData({name:'phone xyz'});
}); });
}); });
}); });
</pre> </pre>
To run the unit tests, execute the `./scripts/test.sh` script and you should see the following To run the unit tests, execute the `./scripts/test.sh` script and you should see the following
output. output.
Chrome: Runner reset. Chrome: Runner reset.
.... ....
Total 4 tests (Passed: 4; Fails: 0; Errors: 0) (3.00 ms) Total 4 tests (Passed: 4; Fails: 0; Errors: 0) (3.00 ms)
Chrome 11.0.696.57 Mac OS: Run 4 tests (Passed: 4; Fails: 0; Errors 0) (3.00 ms) Chrome 11.0.696.57 Mac OS: Run 4 tests (Passed: 4; Fails: 0; Errors 0) (3.00 ms)
# Summary # Summary
There you have it! We have created a web app in a relatively short amount of time. There you have it! We have created a web app in a relatively short amount of time.
<ul doc:tutorial-nav="11"></ul> <ul doc:tutorial-nav="11"></ul>

View file

@ -2,26 +2,20 @@
@name Tutorial: The End @name Tutorial: The End
@description @description
Our application is now complete. Feel free to experiment with the code further, and jump back to Our application is now complete. Feel free to experiment with the code further, and jump back to
previous steps using the `git checkout` or `goto_step.sh` commands. previous steps using the `git checkout` or `goto_step.sh` commands.
For more details and examples of the angular concepts we touched on in this tutorial, see the For more details and examples of the angular concepts we touched on in this tutorial, see the
{@link guide/ Developer Guide}. {@link guide/ Developer Guide}.
For several more examples of code, see the {@link cookbook/ Cookbook}. For several more examples of code, see the {@link cookbook/ Cookbook}.
When you are ready to start developing a project using angular, we recommend that you bootstrap When you are ready to start developing a project using angular, we recommend that you bootstrap
your development with the {@link https://github.com/angular/angular-seed angular seed} project. your development with the {@link https://github.com/angular/angular-seed angular seed} project.
We hope this tutorial was useful to you and that you learned enough about angular to make you want We hope this tutorial was useful to you and that you learned enough about angular to make you want
to learn more. We especially hope you are inspired to go out and develop angular web apps of your to learn more. We especially hope you are inspired to go out and develop angular web apps of your
own, and that you might be interested in {@link misc/contribute contributing} to angular. own, and that you might be interested in {@link misc/contribute contributing} to angular.
If you have questions or feedback or just want to say "hi", please post a message at {@link If you have questions or feedback or just want to say "hi", please post a message at {@link
https://groups.google.com/forum/#!forum/angular}. https://groups.google.com/forum/#!forum/angular}.