mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-16 23:30:23 +00:00
docs - stripping extra new lines
This commit is contained in:
parent
d428c9910e
commit
b842642b57
75 changed files with 3 additions and 1723 deletions
|
|
@ -3,11 +3,9 @@
|
|||
@name angular.service
|
||||
@description
|
||||
|
||||
|
||||
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}.
|
||||
|
||||
|
||||
* {@link angular.service.$browser $browser } - Provides an instance of a browser object
|
||||
* {@link angular.service.$cookieStore $cookieStore } - Provides key / value storage backed by
|
||||
session cookies
|
||||
|
|
@ -27,6 +25,5 @@ server-side data sources
|
|||
* {@link angular.service.$window $window } - References the browsers `window` object
|
||||
* {@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
|
||||
guide/dev_guide.services Angular Services} in the angular Developer Guide.
|
||||
|
|
|
|||
|
|
@ -2,10 +2,8 @@
|
|||
@name API Reference
|
||||
@description
|
||||
|
||||
|
||||
## Angular Compiler API
|
||||
|
||||
|
||||
* {@link angular.widget Widgets} - Angular custom DOM element
|
||||
* {@link angular.directive Directives} - Angular DOM element attributes
|
||||
* {@link angular.markup Markup} and {@link angular.attrMarkup Attribute Markup}
|
||||
|
|
@ -14,47 +12,33 @@
|
|||
* {@link angular.validator Validators} - Angular input validators
|
||||
* {@link angular.compile angular.compile()} - Template compiler
|
||||
|
||||
|
||||
## Angular Scope API
|
||||
|
||||
|
||||
* {@link angular.scope Scope Object} - Angular scope object
|
||||
|
||||
|
||||
|
||||
|
||||
## Angular Services & Dependency Injection API
|
||||
|
||||
|
||||
* {@link angular.service Angular Services}
|
||||
* {@link angular.injector angular.injector() }
|
||||
|
||||
|
||||
|
||||
|
||||
## Angular Testing API
|
||||
|
||||
|
||||
* {@link angular.mock Testing Mocks API} - Mock objects for testing
|
||||
* {@link
|
||||
https://docs.google.com/document/d/11L8htLKrh6c92foV71ytYpiKkeKpM4_a5-9c3HywfIc/edit?hl=en_US
|
||||
Angular Scenario Runner} - Automated scenario testing documentation
|
||||
|
||||
|
||||
|
||||
|
||||
## Angular Utility Functions
|
||||
|
||||
|
||||
### HTML & DOM Manipulation
|
||||
|
||||
|
||||
* {@link angular.element angular.element()}
|
||||
|
||||
|
||||
### Misc
|
||||
|
||||
|
||||
* {@link angular.bind angular.bind() }
|
||||
* {@link angular.extend angular.extend() }
|
||||
* {@link angular.forEach angular.forEach() }
|
||||
|
|
@ -62,11 +46,8 @@ Angular Scenario Runner} - Automated scenario testing documentation
|
|||
* {@link angular.noop angular.noop() }
|
||||
|
||||
|
||||
|
||||
|
||||
## Type Identification
|
||||
|
||||
|
||||
* {@link angular.isArray angular.isArray() }
|
||||
* {@link angular.isDate angular.isDate() }
|
||||
* {@link angular.isDefined angular.isDefined() }
|
||||
|
|
@ -76,25 +57,18 @@ Angular Scenario Runner} - Automated scenario testing documentation
|
|||
* {@link angular.isString angular.isString() }
|
||||
* {@link angular.isUndefined angular.isUndefined() }
|
||||
|
||||
|
||||
## Strings
|
||||
|
||||
|
||||
* {@link angular.lowercase angular.lowercase() }
|
||||
* {@link angular.uppercase angular.uppercase() }
|
||||
|
||||
|
||||
### JSON
|
||||
|
||||
|
||||
* {@link angular.fromJson angular.fromJson() }
|
||||
* {@link angular.toJson angular.toJson() }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Utility methods for JavaScript types
|
||||
* {@link angular.Object Object API} - Utility functions for JavaScript objects
|
||||
* {@link angular.Array Array API} - Utility functions for JavaScript arrays
|
||||
|
|
|
|||
|
|
@ -3,11 +3,9 @@
|
|||
@name Cookbook: Advanced Form
|
||||
@description
|
||||
|
||||
|
||||
Here we extend the basic form example to include common features such as reverting, dirty state
|
||||
detection, and preventing invalid form submission.
|
||||
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script>
|
||||
|
|
@ -31,13 +29,11 @@ detection, and preventing invalid form submission.
|
|||
this.cancel();
|
||||
}
|
||||
|
||||
|
||||
UserForm.prototype = {
|
||||
cancel: function(){
|
||||
this.form = angular.copy(this.master);
|
||||
},
|
||||
|
||||
|
||||
save: function(){
|
||||
this.master = this.form;
|
||||
this.cancel();
|
||||
|
|
@ -46,11 +42,9 @@ detection, and preventing invalid form submission.
|
|||
</script>
|
||||
<div ng:controller="UserForm">
|
||||
|
||||
|
||||
<label>Name:</label><br/>
|
||||
<input type="text" name="form.name" ng:required/> <br/><br/>
|
||||
|
||||
|
||||
<label>Address:</label><br/>
|
||||
<input type="text" name="form.address.line1" size="33" ng:required/> <br/>
|
||||
<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
|
||||
ng:validate="regexp:zip"/><br/><br/>
|
||||
|
||||
|
||||
<label>Contacts:</label>
|
||||
[ <a href="" ng:click="form.contacts.$add()">add</a> ]
|
||||
<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() ||
|
||||
master.$equals(form)}}">Save</button>
|
||||
|
||||
|
||||
<hr/>
|
||||
Debug View:
|
||||
<pre>form={{form}}
|
||||
|
|
@ -104,11 +96,8 @@ master.$equals(form)}}">Save</button>
|
|||
</doc:example>
|
||||
|
||||
|
||||
|
||||
|
||||
#Things to notice
|
||||
|
||||
|
||||
* Cancel & save buttons are only enabled if the form is dirty — there is something to cancel or
|
||||
save.
|
||||
* Save button is only enabled if there are no validation errors on the form.
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
@name Cookbook: Resources - Buzz
|
||||
@description
|
||||
|
||||
|
||||
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
|
||||
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}
|
||||
to retrieve Buzz activity and comments.
|
||||
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script>
|
||||
|
|
|
|||
|
|
@ -3,18 +3,14 @@
|
|||
@name Cookbook: Deep Linking
|
||||
@description
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
# Assumptions
|
||||
|
||||
|
||||
Your application consists of a single HTML page which bootstraps the application. We will refer
|
||||
to this page as the chrome.
|
||||
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
|
||||
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
|
||||
controller.
|
||||
|
||||
|
||||
# Example
|
||||
|
||||
|
||||
In this example we have a simple app which consist of two screens:
|
||||
|
||||
|
||||
* Welcome: url `#` Show the user contact information.
|
||||
* Settings: url `#/settings` Show an edit screen for user contact information.
|
||||
|
||||
|
||||
|
||||
|
||||
The two partials are defined in the following URLs:
|
||||
|
||||
|
||||
* {@link ./examples/settings.html}
|
||||
* {@link ./examples/welcome.html}
|
||||
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script>
|
||||
|
|
@ -59,7 +47,6 @@ The two partials are defined in the following URLs:
|
|||
$route.when("/settings", {template:'./examples/settings.html', controller:SettingsCntl});
|
||||
$route.parent(this);
|
||||
|
||||
|
||||
// initialize the model to something useful
|
||||
this.person = {
|
||||
name:'anonymous',
|
||||
|
|
@ -67,7 +54,6 @@ The two partials are defined in the following URLs:
|
|||
};
|
||||
}
|
||||
|
||||
|
||||
function WelcomeCntl($route){}
|
||||
WelcomeCntl.prototype = {
|
||||
greet: function(){
|
||||
|
|
@ -75,7 +61,6 @@ The two partials are defined in the following URLs:
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
function SettingsCntl(){
|
||||
this.cancel();
|
||||
}
|
||||
|
|
@ -84,7 +69,6 @@ The two partials are defined in the following URLs:
|
|||
this.form = angular.copy(this.person);
|
||||
},
|
||||
|
||||
|
||||
save: function(){
|
||||
angular.copy(this.form, this.person);
|
||||
window.location.hash = "#";
|
||||
|
|
@ -117,12 +101,8 @@ The two partials are defined in the following URLs:
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# Things to notice
|
||||
|
||||
|
||||
* 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
|
||||
routes.
|
||||
|
|
|
|||
|
|
@ -3,14 +3,11 @@
|
|||
@name Cookbook: Form
|
||||
@description
|
||||
|
||||
|
||||
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
|
||||
allow a user to enter data.
|
||||
|
||||
|
||||
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script>
|
||||
|
|
@ -26,11 +23,9 @@ allow a user to enter data.
|
|||
</script>
|
||||
<div ng:controller="FormController" class="example">
|
||||
|
||||
|
||||
<label>Name:</label><br/>
|
||||
<input type="text" name="user.name" ng:required/> <br/><br/>
|
||||
|
||||
|
||||
<label>Address:</label><br/>
|
||||
<input type="text" name="user.address.line1" size="33" ng:required/> <br/>
|
||||
<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
|
||||
ng:validate="regexp:zip"/><br/><br/>
|
||||
|
||||
|
||||
<label>Phone:</label>
|
||||
[ <a href="" ng:click="user.contacts.$add()">add</a> ]
|
||||
<div ng:repeat="contact in user.contacts">
|
||||
|
|
@ -56,7 +50,6 @@ ng:validate="regexp:zip"/><br/><br/>
|
|||
<pre>user={{user}}</pre>
|
||||
</div>
|
||||
|
||||
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should show debug', function(){
|
||||
|
|
@ -69,13 +62,11 @@ ng:validate="regexp:zip"/><br/><br/>
|
|||
expect(binding('user')).toMatch(/you@example.org/);
|
||||
});
|
||||
|
||||
|
||||
it('should remove contact', function(){
|
||||
using('.example').element('a:contains(X)').click();
|
||||
expect(binding('user')).not().toMatch(/\(234\) 555\-1212/);
|
||||
});
|
||||
|
||||
|
||||
it('should validate zip', function(){
|
||||
expect(using('.example').element(':input[name=user.address.zip]').attr('className'))
|
||||
.not().toMatch(/ng-validation-error/);
|
||||
|
|
@ -84,7 +75,6 @@ ng:validate="regexp:zip"/><br/><br/>
|
|||
.toMatch(/ng-validation-error/);
|
||||
});
|
||||
|
||||
|
||||
it('should validate state', function(){
|
||||
expect(using('.example').element(':input[name=user.address.state]').attr('className'))
|
||||
.not().toMatch(/ng-validation-error/);
|
||||
|
|
@ -96,11 +86,8 @@ ng:validate="regexp:zip"/><br/><br/>
|
|||
</doc:example>
|
||||
|
||||
|
||||
|
||||
|
||||
# Things to notice
|
||||
|
||||
|
||||
* The user data model is initialized {@link api/angular.directive.ng:controller controller} and is
|
||||
available in
|
||||
the {@link api/angular.scope scope} with the initial data.
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
@name Cookbook: Hello World
|
||||
@description
|
||||
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
Your name: <input type="text" name="name" value="World"/>
|
||||
|
|
@ -19,13 +18,10 @@
|
|||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
|
||||
# Things to notice
|
||||
|
||||
|
||||
Take a look through the source and note:
|
||||
|
||||
|
||||
* 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.
|
||||
* No need for listener registration and event firing on change events.
|
||||
|
|
|
|||
|
|
@ -3,68 +3,48 @@
|
|||
@name Cookbook
|
||||
@description
|
||||
|
||||
|
||||
Welcome to the angular cookbook. Here we will show you typical uses of angular by example.
|
||||
|
||||
|
||||
|
||||
|
||||
# Hello World
|
||||
|
||||
|
||||
{@link helloworld Hello World}: The simplest possible application that demonstrates the
|
||||
classic Hello World!
|
||||
|
||||
|
||||
|
||||
|
||||
# Basic Form
|
||||
|
||||
|
||||
{@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.
|
||||
|
||||
|
||||
|
||||
|
||||
# Advanced Form
|
||||
|
||||
|
||||
{@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
|
||||
validation errors exist.
|
||||
|
||||
|
||||
|
||||
|
||||
# Model View Controller
|
||||
|
||||
|
||||
{@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
|
||||
separation aids in maintainability and testability of your project.
|
||||
|
||||
|
||||
|
||||
|
||||
# Multi-page App and Deep Linking
|
||||
|
||||
|
||||
{@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
|
||||
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.
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
|
||||
|
||||
# Services
|
||||
|
||||
|
||||
{@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
|
||||
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).
|
||||
|
||||
|
||||
|
||||
|
||||
# External Resources
|
||||
|
||||
|
||||
{@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
|
||||
specially tailored to angular data binding.
|
||||
|
|
|
|||
|
|
@ -3,19 +3,15 @@
|
|||
@name Cookbook: MVC
|
||||
@description
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
isolation since one can simply instantiate the controller and test without a view, because there is
|
||||
no connection between the controller and the view.
|
||||
|
||||
|
||||
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script>
|
||||
|
|
@ -83,7 +79,6 @@ no connection between the controller and the view.
|
|||
};
|
||||
</script>
|
||||
|
||||
|
||||
<h3>Tic-Tac-Toe</h3>
|
||||
<div ng:controller="TicTacToeCntl">
|
||||
Next Player: {{nextMove}}
|
||||
|
|
@ -109,7 +104,6 @@ no connection between the controller and the view.
|
|||
expect(element('.winner').text()).toEqual('Player X has won!');
|
||||
});
|
||||
|
||||
|
||||
function piece(row, col) {
|
||||
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>
|
||||
|
||||
|
||||
|
||||
|
||||
# Things to notice
|
||||
|
||||
|
||||
* 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 can be instantiated in isolation (without a view) and the code will still execute.
|
||||
|
|
|
|||
|
|
@ -3,30 +3,23 @@
|
|||
@name Developer Guide: Initializing Angular: Automatic Initiialization
|
||||
@description
|
||||
|
||||
|
||||
Angular initializes automatically when you load the angular script into your page, specifying
|
||||
angular's `ng:autobind` attribute with no arguments:
|
||||
|
||||
|
||||
<script src="angular.js" ng:autobind>
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
global namespace. Everything that angular subsequently creates is bound to fields in this global
|
||||
object.
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
logic.
|
||||
|
||||
|
||||
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
|
||||
changes.
|
||||
|
||||
|
||||
For details on how the compiler works, see {@link dev_guide.compiler Angular HTML Compiler}.
|
||||
|
||||
|
||||
|
||||
|
||||
## Initialization Options
|
||||
|
||||
|
||||
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
|
||||
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:
|
||||
|
||||
|
||||
ng:autobind="angularContent"
|
||||
|
||||
|
||||
|
||||
|
||||
## Auto-bootstrap with `#autobind`
|
||||
|
||||
|
||||
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
|
||||
appending `#autobind` to the `<script src=...>` URL, like in this snippet:
|
||||
|
||||
|
||||
<pre>
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
|
@ -82,19 +65,15 @@ appending `#autobind` to the `<script src=...>` URL, like in this snippet:
|
|||
</html>
|
||||
</pre>
|
||||
|
||||
|
||||
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`.
|
||||
|
||||
|
||||
## Filename Restrictions for Auto-bootstrap
|
||||
|
||||
|
||||
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
|
||||
conventions:
|
||||
|
||||
|
||||
- `angular.js`
|
||||
- `angular-min.js`
|
||||
- `angular-x.x.x.js`
|
||||
|
|
@ -103,28 +82,20 @@ conventions:
|
|||
- `angular-x.x.x-xxxxxxxx.min.js` (dev snapshot)
|
||||
- `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
|
||||
ends with `/`.
|
||||
|
||||
|
||||
## Global Angular Object
|
||||
|
||||
|
||||
The angular script creates a single global variable `angular` in the global namespace. All angular
|
||||
APIs are bound to fields of this global object.
|
||||
|
||||
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
|
||||
* {@link dev_guide.bootstrap Initializing Angular}
|
||||
* {@link dev_guide.bootstrap.manual_bootstrap Manual Initialization}
|
||||
|
||||
|
||||
## Related API
|
||||
|
||||
|
||||
{@link api/angular.compile Compiler API}
|
||||
|
|
|
|||
|
|
@ -3,17 +3,14 @@
|
|||
@name Developer Guide: Initializing Angular: Manual Initialization
|
||||
@description
|
||||
|
||||
|
||||
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
|
||||
the manual bootstrapping method instead.
|
||||
|
||||
|
||||
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
|
||||
explicitly.
|
||||
|
||||
|
||||
<pre>
|
||||
<!doctype html>
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
|
|
@ -32,27 +29,20 @@ Hello {{'World'}}!
|
|||
</html>
|
||||
</pre>
|
||||
|
||||
|
||||
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
|
||||
the document.
|
||||
2. Run angular's {@link dev_guide.compiler Angular HTML compiler}, which converts a template into
|
||||
an executable, bi-directionally bound application.
|
||||
|
||||
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
|
||||
* {@link dev_guide.bootstrap Initializing Angular}
|
||||
* {@link dev_guide.bootstrap.auto_bootstrap Automatic Initialization}
|
||||
* {@link dev_guide.compiler Angular HTML compiler}
|
||||
|
||||
|
||||
## Related API
|
||||
|
||||
|
||||
{@link api/angular.compile Compiler API}
|
||||
|
|
|
|||
|
|
@ -3,20 +3,16 @@
|
|||
@name Developer Guide: Initializing Angular
|
||||
@description
|
||||
|
||||
|
||||
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:
|
||||
|
||||
|
||||
* Specify the angular namespace in the `<html>` page
|
||||
* 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`)
|
||||
|
||||
|
||||
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:
|
||||
|
||||
|
||||
<pre>
|
||||
<!doctype html>
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
|
|
@ -30,57 +26,40 @@ and manage the whole page. You do this as follows:
|
|||
</pre>
|
||||
|
||||
|
||||
|
||||
|
||||
## Specifying the Angular Namespace
|
||||
|
||||
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
|
||||
|
||||
You need to declare the angular namespace declaration in the following cases:
|
||||
|
||||
|
||||
* 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
|
||||
properly for either HTML or XHTML).
|
||||
|
||||
|
||||
|
||||
|
||||
## Creating Your Own Namespaces
|
||||
|
||||
|
||||
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
|
||||
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`.
|
||||
To create your own namespace, simply add another `xmlns` tag to your page, create an alias, and set
|
||||
it to your unique domain:
|
||||
|
||||
|
||||
<html xmlns:ng="http://angularjs.org" xmlns:my="http://mydomain.com">
|
||||
|
||||
|
||||
|
||||
|
||||
## Loading the Angular Bootstrap 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].min.js - This is a compressed and obfuscated file, suitable for use in
|
||||
production.
|
||||
|
||||
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
|
||||
* {@link dev_guide.bootstrap.auto_bootstrap Automatic Initialization}
|
||||
* {@link dev_guide.bootstrap.manual_bootstrap Manual Initialization}
|
||||
|
|
|
|||
|
|
@ -3,12 +3,10 @@
|
|||
@name Developer Guide: Angular HTML Compiler: Directives: Creating Custom Angular Directives
|
||||
@description
|
||||
|
||||
|
||||
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
|
||||
simplified definition of the built-in {@link api/angular.directive.ng:bind ng:bind} directive:
|
||||
|
||||
|
||||
<pre>
|
||||
angular.directive('ng:bind', function(expression, compiledElement) {
|
||||
var compiler = this;
|
||||
|
|
@ -21,29 +19,21 @@ angular.directive('ng:bind', function(expression, compiledElement) {
|
|||
});
|
||||
</pre>
|
||||
|
||||
|
||||
# Additional Compiler Methods for Custom Directives
|
||||
|
||||
|
||||
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
|
||||
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}.
|
||||
|
||||
|
||||
|
||||
|
||||
## Related Docs
|
||||
|
||||
|
||||
* {@link dev_guide.compiler.directives Understanding Angular Directives}
|
||||
* {@link dev_guide.compiler.directives_widgets Comparing Directives and Widgets}
|
||||
* {@link dev_guide.compiler Angular HTML Compiler}
|
||||
|
||||
|
||||
|
||||
|
||||
## Related API
|
||||
|
||||
|
||||
* {@link api/angular.directive Angular Directive API}.
|
||||
|
|
|
|||
|
|
@ -3,39 +3,30 @@
|
|||
@name Developer Guide: Angular HTML Compiler: Understanding Angular Directives
|
||||
@description
|
||||
|
||||
|
||||
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
|
||||
following example, the angular directive (`ng:controller`) is a div tag:
|
||||
|
||||
|
||||
<div ng:controller>
|
||||
|
||||
|
||||
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
|
||||
directives per element.
|
||||
|
||||
|
||||
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:
|
||||
|
||||
|
||||
<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
|
||||
directive. The `ng:click` directive lets you implement custom behavior in an associated controller
|
||||
function.
|
||||
|
||||
|
||||
In the next example, we add the {@link api/angular.directive.ng:bind ng:bind} directive to a
|
||||
`<span>` tag:
|
||||
|
||||
|
||||
<span ng:bind="1+2"></span>
|
||||
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
|
||||
* {@link dev_guide.compiler Angular HTML Compiler}
|
||||
* {@link dev_guide.compiler.directives.creating_directives Creating Angular Directives}
|
||||
* {@link dev_guide.compiler.directives_widgets Comparing Directives and Widgets}
|
||||
|
||||
|
||||
## Related API:
|
||||
|
||||
|
||||
* {@link api/angular.directive Directive API}
|
||||
* {@link api/angular.widget Widget API}
|
||||
|
|
|
|||
|
|
@ -3,18 +3,15 @@
|
|||
@name Developer Guide: Angular HTML Compiler: Comparing Directives and Attribute Widgets
|
||||
@description
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
custom directives and widgets.
|
||||
|
||||
|
||||
For example, consider this piece of HTML, which uses the `ng:repeat`, `ng:init`, and `ng:bind`
|
||||
widget and directives:
|
||||
|
||||
|
||||
<pre>
|
||||
<ul ng:init="people=['mike', 'mary']">
|
||||
<li ng:repeat="person in people"
|
||||
|
|
@ -24,36 +21,28 @@ widget and directives:
|
|||
</ul>
|
||||
</pre>
|
||||
|
||||
|
||||
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
|
||||
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}
|
||||
for each of the `<li>`copies.
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
priority than others, use a widget for the attribute that must be processed first.
|
||||
|
||||
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
|
||||
* {@link dev_guide.compiler.directives Understanding Angular Directives}
|
||||
* {@link dev_guide.compiler.widgets Understanding Angular Widgets}
|
||||
|
||||
|
||||
## Related API:
|
||||
|
||||
|
||||
* {@link api/angular.directive Directive API}
|
||||
* {@link api/angular.widget Widget API}
|
||||
|
|
|
|||
|
|
@ -65,17 +65,13 @@ return function(linkElement){
|
|||
Note: For more about widgets, see {@link dev_guide.compiler.widgets Understanding Angular Widgets}
|
||||
and the {@link api/angular.widget widget API reference page}.
|
||||
|
||||
|
||||
# Compilation process for `<my:greeter>`
|
||||
|
||||
|
||||
Here are the steps that the compiler takes in processing the page that contains the widget
|
||||
definition above:
|
||||
|
||||
|
||||
## Compile Phase
|
||||
|
||||
|
||||
1. Recursively traverse the DOM depth-first.
|
||||
2. Find the angular.widget definition.
|
||||
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.
|
||||
4. Return the aggregate link function, which includes just one link function in this example.
|
||||
|
||||
|
||||
## Link Phase
|
||||
|
||||
|
||||
1. Execute the aggregate link function, which includes the following steps:
|
||||
1. Create a <span> element set to the salutation class
|
||||
2. Create a <span> element set to the name class.
|
||||
|
|
@ -99,17 +93,12 @@ compileElement, because that's the template.)
|
|||
corresponding spans.
|
||||
|
||||
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
|
||||
* {@link dev_guide.compiler Angular HTML Compiler}
|
||||
* {@link dev_guide.compiler.understanding_compiler Understanding How the Compiler Works}
|
||||
* {@link dev_guide.compiler.testing_dom_element Testing a New DOM Element}
|
||||
|
||||
|
||||
## Related API
|
||||
|
||||
|
||||
* {@link api/angular.compile angular.compile()}
|
||||
|
|
|
|||
|
|
@ -3,59 +3,46 @@
|
|||
@name Developer Guide: Angular HTML Compiler: Understanding Angular Markup
|
||||
@description
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
{@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
|
||||
points (between the model and view) for angular expressions. You can also create your own custom
|
||||
markup.
|
||||
|
||||
|
||||
# Using Double Curly-brace Markup (`{{ }}`)
|
||||
|
||||
|
||||
The double curly-brace (`{{ }}`) markup translates an enclosed expression into an {@link
|
||||
api/angular.directive.ng:bind ng:bind} directive:
|
||||
|
||||
|
||||
<pre>
|
||||
{{expression}}
|
||||
</pre>
|
||||
|
||||
|
||||
is transformed to:
|
||||
|
||||
|
||||
<pre>
|
||||
<span ng:bind="expression"></span>
|
||||
</pre>
|
||||
|
||||
|
||||
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
|
||||
expanded elements are then {@link dev_guide.compiler compiled} normally.
|
||||
|
||||
|
||||
|
||||
|
||||
# Creating Custom Markup
|
||||
|
||||
|
||||
Let's say you want to define markup that transforms `---` into a horizontal rule (`<hr/>`):
|
||||
|
||||
|
||||
<pre>
|
||||
header
|
||||
---
|
||||
footer
|
||||
</pre>
|
||||
|
||||
|
||||
should translate to:
|
||||
<pre>
|
||||
header
|
||||
|
|
@ -63,10 +50,8 @@ header
|
|||
footer
|
||||
</pre>
|
||||
|
||||
|
||||
Here is how you could extend the angular compiler to create the "---" markup:
|
||||
|
||||
|
||||
<pre>
|
||||
angular.markup('---', function(text, textNode, parentElement) {
|
||||
var compiler = this;
|
||||
|
|
@ -83,20 +68,16 @@ angular.markup('---', function(text, textNode, parentElement) {
|
|||
});
|
||||
</pre>
|
||||
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
|
||||
## Attribute Markup
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
<pre>
|
||||
angular.attrMarkup('extraClass', function(attrValue, attrName, element){
|
||||
if (attrName == 'additional-class') {
|
||||
|
|
@ -106,15 +87,10 @@ angular.attrMarkup('extraClass', function(attrValue, attrName, element){
|
|||
</pre>
|
||||
|
||||
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
|
||||
* {@link dev_guide.compiler Angular HTML Compiler}
|
||||
|
||||
|
||||
## Related API
|
||||
|
||||
|
||||
* {@link api/angular.compile Compiler API Reference}
|
||||
|
|
|
|||
|
|
@ -3,24 +3,18 @@
|
|||
@name Developer Guide: Angular HTML Compiler
|
||||
@description
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
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
|
||||
specific types of web applications.
|
||||
|
||||
|
||||
All compilation takes place in the web browser; no server is involved.
|
||||
|
||||
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
|
||||
* {@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.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.markup Understanding Angular Markup}
|
||||
|
||||
|
||||
## Related API
|
||||
|
||||
|
||||
* {@link api/angular.compile Angular Compiler API}
|
||||
|
|
|
|||
|
|
@ -4,22 +4,15 @@
|
|||
@description
|
||||
|
||||
|
||||
|
||||
|
||||
"Testing, testing, come in, over?"
|
||||
|
||||
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
|
||||
* {@link dev_guide.compiler Angular HTML Compiler}
|
||||
* {@link dev_guide.compiler.understanding_compiler Understanding How the Compiler Works}
|
||||
* {@link dev_guide.compiler.extending_compiler Extending the Angular Compiler}
|
||||
|
||||
|
||||
## Related API
|
||||
|
||||
|
||||
* {@link api/angular.compile angular.compile()}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
@name Developer Guide: Angular HTML Compiler: Understanding How the Compiler Works
|
||||
@description
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
dev_guide.bootstrap Initializing Angular}.)
|
||||
|
||||
|
||||
The compile and link functions are related as follows:
|
||||
|
||||
|
||||
* **compile function** — Registers a listener for the widget, directive, or markup expression. The
|
||||
compiler calls this function exactly once.
|
||||
* **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
|
||||
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
|
||||
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
|
||||
Compiler API} for details.
|
||||
|
||||
|
||||
When the angular compiler compiles a page, it proceeds through 3 phases: Compile, Create Root
|
||||
Scope, and Link:
|
||||
|
||||
|
||||
1. Compile Phase
|
||||
|
||||
|
||||
1. Recursively traverse the DOM, depth-first.
|
||||
2. Look for a matching compile function of type widget, then markup, then directive.
|
||||
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.
|
||||
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
|
||||
individual link functions.
|
||||
|
||||
|
||||
2. Create Root Scope Phase
|
||||
|
||||
|
||||
* Inject all services into the root scope.
|
||||
|
||||
|
||||
3. Link Phase
|
||||
|
||||
|
||||
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.
|
||||
2. If there are any clones of the DOM caused by repeating elements, call the link function
|
||||
multiple times, one for each repeating item.
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
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
|
||||
Compiler API doc}.
|
||||
|
||||
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
|
||||
* {@link dev_guide.compiler Angular HTML Compiler}
|
||||
* {@link dev_guide.compiler.extending_compiler Extending the Angular Compiler}
|
||||
* {@link dev_guide.compiler.testing_dom_element Testing a New DOM Element}
|
||||
|
||||
|
||||
## Related API
|
||||
|
||||
|
||||
* {@link api/angular.compile angular.compile()}
|
||||
|
|
|
|||
|
|
@ -3,24 +3,19 @@
|
|||
@name Developer Guide: Angular HTML Compiler: Widgets: Creating Custom Widgets
|
||||
@description
|
||||
|
||||
|
||||
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.)
|
||||
|
||||
|
||||
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:
|
||||
|
||||
|
||||
<pre>
|
||||
// An element widget
|
||||
<my:watch exp="name"/>
|
||||
</pre>
|
||||
|
||||
|
||||
You can implement `my:watch` like this:
|
||||
|
||||
|
||||
<pre>
|
||||
angular.widget('my:watch', function(compileElement) {
|
||||
var compiler = this;
|
||||
|
|
@ -35,15 +30,11 @@ angular.widget('my:watch', function(compileElement) {
|
|||
</pre>
|
||||
|
||||
|
||||
|
||||
|
||||
# Creating a Custom Attribute Widget
|
||||
|
||||
|
||||
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:
|
||||
|
||||
|
||||
<pre>
|
||||
// An attribute widget (my-watch) in a div tag
|
||||
<div my-watch="name">text</div>
|
||||
|
|
@ -62,11 +53,8 @@ angular.widget('@my:watch', function(expression, compileElement) {
|
|||
</pre>
|
||||
|
||||
|
||||
|
||||
|
||||
# Live Example of a Custom Element Widget
|
||||
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script>
|
||||
|
|
@ -88,29 +76,21 @@ angular.widget('my:time', function(compileElement){
|
|||
</doc:example>
|
||||
|
||||
|
||||
|
||||
|
||||
# Additional Compiler Methods for Custom Widgets
|
||||
|
||||
|
||||
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
|
||||
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}.
|
||||
|
||||
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
|
||||
* {@link dev_guide.compiler Angular HTML Compiler}
|
||||
* {@link dev_guide.compiler.directives Angular Directives}
|
||||
* {@link dev_guide.compiler.widgets Angular Widgets}
|
||||
* {@link dev_guide.compiler.directives.creating_directives Creating Custom Directives}
|
||||
|
||||
|
||||
## Related API
|
||||
|
||||
|
||||
* {@link api/angular.compile Compiler API}
|
||||
|
|
|
|||
|
|
@ -3,24 +3,19 @@
|
|||
@name Developer Guide: Angular HTML Compiler: Understanding Angular Widgets
|
||||
@description
|
||||
|
||||
|
||||
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
|
||||
own custom widgets.
|
||||
|
||||
|
||||
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
|
||||
properties).
|
||||
|
||||
|
||||
Widgets come in two types:
|
||||
|
||||
|
||||
* 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}.
|
||||
|
||||
|
||||
* 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
|
||||
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
|
||||
|
||||
|
||||
* {@link dev_guide.compiler Angular HTML Compiler}
|
||||
* {@link dev_guide.compiler.directives Angular Directives}
|
||||
* {@link dev_guide.compiler.widgets.creating_widgets Creating Custom Widgets}
|
||||
* {@link dev_guide.compiler.directives.creating_directives Creating Custom Directives}
|
||||
|
||||
|
||||
## Related API
|
||||
|
||||
|
||||
* {@link api/angular.compile Compiler API}
|
||||
|
|
|
|||
|
|
@ -3,41 +3,31 @@
|
|||
@name Developer Guide: About Dependency Injection (DI)
|
||||
@description
|
||||
|
||||
|
||||
Dependency Injection (DI) is an object-oriented software design pattern that supports the
|
||||
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
|
||||
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
|
||||
(or injector) component manages the life cycles and dependencies for all of the other components in
|
||||
an application.
|
||||
|
||||
|
||||
Angular has a built-in dependency management subsystem that helps to make your applications easier
|
||||
to develop, understand, and test.
|
||||
|
||||
|
||||
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
|
||||
Inversion of Control} by Martin Fowler, or read about DI in your favorite software design pattern
|
||||
book.
|
||||
|
||||
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
|
||||
* {@link dev_guide.di.understanding_di Understanding DI in Angular}
|
||||
* {@link dev_guide.services Angular Services}
|
||||
|
||||
|
||||
|
||||
|
||||
## Related API
|
||||
|
||||
|
||||
* {@link api/angular.service Service API}
|
||||
* {@link api/angular.injector Angular Injector API}
|
||||
|
|
|
|||
|
|
@ -4,21 +4,16 @@
|
|||
@description
|
||||
|
||||
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
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
|
||||
services}.
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
events:
|
||||
|
||||
|
||||
<img src="img/guide/di_sequence_final.png">
|
||||
|
||||
|
||||
In the illustration above, the dependency injection sequence proceeds as follows:
|
||||
|
||||
|
||||
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,
|
||||
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
|
||||
|
||||
|
||||
|
||||
|
||||
## How Scope Relates to DI
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
achieve the necessary isolation by having each test create its own separate root scope.
|
||||
|
||||
|
||||
<pre>
|
||||
// create a root scope
|
||||
var rootScope = angular.scope();
|
||||
|
|
@ -68,50 +55,40 @@ var rootScope = angular.scope();
|
|||
var myService = rootScope.$service('myService');
|
||||
</pre>
|
||||
|
||||
|
||||
## 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
|
||||
this section for drawbacks.
|
||||
|
||||
|
||||
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
|
||||
function declaration along with the argument names as shown below:
|
||||
|
||||
|
||||
<pre>
|
||||
function myFn(a,b){}
|
||||
expect(myFn.toString()).toEqual('function myFn(a,b){}');
|
||||
</pre>
|
||||
|
||||
|
||||
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
|
||||
equivalent:
|
||||
|
||||
|
||||
<pre>
|
||||
// given a user defined service
|
||||
angular.service('serviceA', ...);
|
||||
|
||||
|
||||
// inject '$window', 'serviceA', curry 'name';
|
||||
function fnA($window, serviceA, name){};
|
||||
fnA.$inject = ['$window', 'serviceA'];
|
||||
|
||||
|
||||
// inject '$window', 'serviceA', curry 'name';
|
||||
function fnB($window, serviceA_, name){};
|
||||
// implies: fnB.$inject = ['$window', 'serviceA'];
|
||||
</pre>
|
||||
|
||||
|
||||
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:
|
||||
|
||||
|
||||
* Any argument starting with `$` is an angular service and will be added to the `$inject` property
|
||||
array
|
||||
* 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 `_`
|
||||
(these are free arguments for {@link http://en.wikipedia.org/wiki/Currying currying})
|
||||
|
||||
|
||||
**IMPORTANT**
|
||||
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
|
||||
|
|
@ -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.
|
||||
|
||||
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
|
||||
* {@link dev_guide.services Angular Services}
|
||||
|
||||
|
||||
## Related API
|
||||
|
||||
|
||||
* {@link api/angular.service Services API}
|
||||
|
|
|
|||
|
|
@ -3,11 +3,9 @@
|
|||
@name Developer Guide: DI: Using DI in Controllers
|
||||
@description
|
||||
|
||||
|
||||
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:
|
||||
|
||||
|
||||
<pre>
|
||||
function MyController($route){
|
||||
// configure the route service
|
||||
|
|
@ -16,14 +14,12 @@ function MyController($route){
|
|||
MyController.$inject = ['$route'];
|
||||
</pre>
|
||||
|
||||
|
||||
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
|
||||
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 from the HTML template, as follows:
|
||||
|
||||
|
||||
<pre>
|
||||
<!doctype html>
|
||||
<html xmlns:ng="http://angularjs.org" ng:controller="MyController">
|
||||
|
|
@ -34,33 +30,25 @@ controller from the HTML template, as follows:
|
|||
</html>
|
||||
</pre>
|
||||
|
||||
|
||||
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,
|
||||
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.
|
||||
|
||||
|
||||
<pre>
|
||||
MyController.$inject = ['$route'];
|
||||
</pre>
|
||||
|
||||
|
||||
The information in `$inject` is then used by the {@link api/angular.injector injector} to call the
|
||||
function with the correct arguments.
|
||||
|
||||
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
|
||||
* {@link dev_guide.di About Dependency Injection}
|
||||
* {@link dev_guide.di.understanding_di Understanding Dependency Injection in Angular}
|
||||
* {@link dev_guide.services Angular Services}
|
||||
|
||||
|
||||
## Related API
|
||||
|
||||
|
||||
* {@link api/angular.injector Angular Injector API}
|
||||
|
|
|
|||
|
|
@ -3,31 +3,24 @@
|
|||
@name Developer Guide: Understanding Angular Expressions
|
||||
@description
|
||||
|
||||
|
||||
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
|
||||
equivalent to JavaScript expressions.
|
||||
|
||||
|
||||
For example, these are all valid expressions in angular:
|
||||
|
||||
|
||||
* `1+2={{1+2}}`
|
||||
* `3*10|currency`
|
||||
* `Hello {{name}}!`
|
||||
* `Hello {{'World'}}!`
|
||||
|
||||
|
||||
|
||||
|
||||
## Angular Expressions vs. JS Expressions
|
||||
|
||||
|
||||
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
|
||||
think of angular expressions as JavaScript expressions with these differences:
|
||||
|
||||
|
||||
* **Attribute Evaluation:** evaluation of all attributes are against the current scope, not to the
|
||||
global window as 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.
|
||||
* **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
|
||||
controller method and call that. If you want to `eval()` an angular expression from JavaScript, use
|
||||
the `Scope:$eval()` method.
|
||||
|
||||
|
||||
## Example
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
|
|
@ -56,10 +47,8 @@ the `Scope:$eval()` method.
|
|||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
|
||||
You can try evaluating different expressions here:
|
||||
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<div ng:init="exprs=[]" class="expressions">
|
||||
|
|
@ -85,18 +74,14 @@ You can try evaluating different expressions here:
|
|||
</doc:example>
|
||||
|
||||
|
||||
|
||||
|
||||
# Attribute Evaluation
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
the global state (a common source of subtle bugs).
|
||||
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<div class="example2" ng:init="$window = $service('$window')">
|
||||
|
|
@ -121,67 +106,50 @@ the global state (a common source of subtle bugs).
|
|||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
|
||||
## Forgiving
|
||||
|
||||
|
||||
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
|
||||
expression evaluations are primarily used for data binding, which often look like this:
|
||||
|
||||
|
||||
{{a.b.c}}
|
||||
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
|
||||
Assignments work the same way in reverse:
|
||||
|
||||
|
||||
a.b.c = 10
|
||||
|
||||
|
||||
...creates the intermediary objects even if a is undefined.
|
||||
|
||||
|
||||
|
||||
|
||||
## No Control Flow Statements
|
||||
|
||||
|
||||
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
|
||||
conditional (including ternary operators), loop, or to throw from a view expression, delegate to a
|
||||
JavaScript method instead.
|
||||
|
||||
|
||||
|
||||
|
||||
## Type Augmentation
|
||||
|
||||
|
||||
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.
|
||||
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
|
||||
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
|
||||
they have extra methods. The actual method for `$filter()` is `angular.Array.filter()`. You can
|
||||
call it from JavaScript.
|
||||
|
||||
|
||||
Extensions: You can further extend the expression vocabulary by adding new methods to
|
||||
`angular.Array` or `angular.String`, etc.
|
||||
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<div ng:init="friends = [
|
||||
|
|
@ -206,48 +174,36 @@ Extensions: You can further extend the expression vocabulary by adding new metho
|
|||
input('searchText').enter('a');
|
||||
expect(tr.count()).toBe(2);
|
||||
|
||||
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
|
||||
## Filters
|
||||
|
||||
|
||||
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
|
||||
according to the locale before displaying it to the user. You can pass expressions through a chain
|
||||
of filters like this:
|
||||
|
||||
|
||||
name | uppercase
|
||||
|
||||
|
||||
The expression evaluator simply passes the value of name to angular.filter.uppercase.
|
||||
|
||||
|
||||
Chain filters using this syntax:
|
||||
|
||||
|
||||
value | filter1 | filter2
|
||||
|
||||
|
||||
You can also pass colon-delimited arguments to filters, for example, to display the number 123 with
|
||||
2 decimal points:
|
||||
|
||||
|
||||
123 | number:2
|
||||
|
||||
|
||||
# The $
|
||||
|
||||
|
||||
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
|
||||
`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
|
||||
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
|
||||
|
|
@ -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.
|
||||
|
||||
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
|
||||
* {@link dev_guide.compiler.markup Understanding Angular Markup}
|
||||
* {@link dev_guide.templates.filters Understanding Angular Filters}
|
||||
|
||||
|
||||
## Related API
|
||||
|
||||
|
||||
* {@link api/angular.compile Angular Compiler API}
|
||||
|
|
|
|||
|
|
@ -3,30 +3,24 @@
|
|||
@name Developer Guide: Introduction
|
||||
@description
|
||||
|
||||
|
||||
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
|
||||
apps easier and faster than ever before.
|
||||
|
||||
|
||||
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
|
||||
care of many of these tasks, including:
|
||||
|
||||
|
||||
* DOM Manipulation
|
||||
* Setting Up Listeners and Notifiers
|
||||
* Input Validation
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
At the same time that angular simplifies the development of web apps, it brings relatively
|
||||
sophisticated techniques to the client-side, including:
|
||||
|
||||
|
||||
* Separation of data, application logic, and presentation components
|
||||
* Data Binding between data and presentation components
|
||||
* 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)
|
||||
* Ease of Testing
|
||||
|
||||
|
||||
These techniques have been for the most part absent from the client-side for far too long.
|
||||
|
||||
|
||||
## Single-page / Round-trip Applications
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
|
|
|
|||
|
|
@ -3,31 +3,23 @@
|
|||
@name Developer Guide: About MVC in Angular
|
||||
@description
|
||||
|
||||
|
||||
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
|
||||
http://en.wikipedia.org/wiki/Model–view–controller MVC} software design pattern into its way of
|
||||
building client-side web applications.
|
||||
|
||||
|
||||
The MVC pattern greatly summarized:
|
||||
|
||||
|
||||
* Separate applications into distinct presentation, data, and logic components
|
||||
* Encourage loose coupling between these components
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
The following topics explain how angular incorporates the MVC pattern into the angular way of
|
||||
developing web applications:
|
||||
|
||||
|
||||
* {@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_view Understanding the View Component}
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -3,56 +3,43 @@
|
|||
@name Developer Guide: About MVC in Angular: Understanding the Controller Component
|
||||
@description
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
controller with the new scope and to augment its behavior.
|
||||
|
||||
|
||||
Use controllers to:
|
||||
|
||||
|
||||
- Set up the initial state of a scope object.
|
||||
- Add behavior to the 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.
|
||||
|
||||
|
||||
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
|
||||
creates instances of the controller type (by invoking the `new` operator on the controller
|
||||
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:
|
||||
|
||||
|
||||
function GreetingCtrl() {
|
||||
this.greeting = 'Hola!';
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
function becomes the scope of the angular scope object, so any assignment to `this` within the
|
||||
controller function happens on the angular 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
|
||||
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
|
||||
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
|
||||
|
|
@ -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.
|
||||
|
||||
|
||||
|
||||
|
||||
# Using Controllers Correctly
|
||||
|
||||
|
||||
In general, a controller shouldn't try to do too much. It should contain only the business logic
|
||||
needed for a single view.
|
||||
|
||||
|
||||
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.
|
||||
This is discussed in the {@link dev_guide.di Dependency Injection} {@link dev_guide.services
|
||||
Services} sections of this guide.
|
||||
|
||||
|
||||
Do not use controllers for:
|
||||
|
||||
|
||||
- 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.
|
||||
Putting any presentation logic into controllers significantly affects testability of the business
|
||||
|
|
@ -95,40 +76,29 @@ services} instead.
|
|||
instances).
|
||||
|
||||
|
||||
|
||||
|
||||
# Associating Controllers with Angular Scope Objects
|
||||
|
||||
|
||||
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
|
||||
directive} or {@link api/angular.service.$route $route service}.
|
||||
|
||||
|
||||
|
||||
|
||||
## Controller Constructor and Methods Example
|
||||
|
||||
|
||||
To illustrate how the controller component works in angular, let's create a little app with the
|
||||
following components:
|
||||
|
||||
|
||||
- A {@link dev_guide.templates template} with two buttons and a simple message
|
||||
- A model consisting of a string named `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
|
||||
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.
|
||||
|
||||
|
||||
|
||||
|
||||
## A Spicy Controller Example
|
||||
|
||||
|
||||
<pre>
|
||||
<body ng:controller="SpicyCtrl">
|
||||
<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>
|
||||
</body>
|
||||
|
||||
|
||||
function SpicyCtrl() {
|
||||
this.spice = 'very';
|
||||
this.chiliSpicy = function() {
|
||||
|
|
@ -144,16 +113,13 @@ function SpicyCtrl() {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
SpicyCtrl.prototype.jalapenoSpicy = function() {
|
||||
this.spice = 'jalapeño';
|
||||
}
|
||||
</pre>
|
||||
|
||||
|
||||
Things to notice in the example above:
|
||||
|
||||
|
||||
- The `ng:controller` directive is used to (implicitly) create a scope for our template, and the
|
||||
scope is augmented (managed) by the `SpicyCtrl` controller.
|
||||
- `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
|
||||
children).
|
||||
|
||||
|
||||
Controller methods can also take arguments, as demonstrated in the following variation of the
|
||||
previous example.
|
||||
|
||||
|
||||
## Controller Method Arguments Example
|
||||
|
||||
|
||||
<pre>
|
||||
<body ng:controller="SpicyCtrl">
|
||||
<input name="customSpice" value="wasabi">
|
||||
|
|
@ -182,7 +145,6 @@ previous example.
|
|||
<p>The food is {{spice}} spicy!</p>
|
||||
</body>
|
||||
|
||||
|
||||
function SpicyCtrl() {
|
||||
this.spice = 'very';
|
||||
this.spicy = function(spice) {
|
||||
|
|
@ -191,22 +153,17 @@ function SpicyCtrl() {
|
|||
}
|
||||
</pre>
|
||||
|
||||
|
||||
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
|
||||
constant `'chili'` in the binding for the first button and a model property `spice` (bound to an
|
||||
input box) in the second button.
|
||||
|
||||
|
||||
|
||||
|
||||
## Controller Inheritance Example
|
||||
|
||||
|
||||
Controller inheritance in angular is based on {@link api/angular.scope Scope} inheritance. Let's
|
||||
have a look at an example:
|
||||
|
||||
|
||||
<pre>
|
||||
<body ng:controller="MainCtrl">
|
||||
<p>Good {{timeOfDay}}, {{name}}!</p>
|
||||
|
|
@ -215,29 +172,24 @@ have a look at an example:
|
|||
<p ng:controller="BabyCtrl">Good {{timeOfDay}}, {{name}}!</p>
|
||||
</body>
|
||||
|
||||
|
||||
function MainCtrl() {
|
||||
this.timeOfDay = 'morning';
|
||||
this.name = 'Nikki';
|
||||
}
|
||||
|
||||
|
||||
function ChildCtrl() {
|
||||
this.name = 'Mattie';
|
||||
}
|
||||
|
||||
|
||||
function BabyCtrl() {
|
||||
this.timeOfDay = 'evening';
|
||||
this.name = 'Gingerbreak Baby';
|
||||
}
|
||||
</pre>
|
||||
|
||||
|
||||
Notice how we nested three `ng:controller` directives in our template. This template construct will
|
||||
result in 4 scopes being created for our view:
|
||||
|
||||
|
||||
- The root scope
|
||||
- The `MainCtrl` scope, which contains `timeOfDay` and `name` models
|
||||
- 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`
|
||||
model defined in the ChildCtrl
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
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
|
||||
are applied to the scope object.
|
||||
|
||||
|
||||
|
||||
|
||||
## Testing Controllers
|
||||
|
||||
|
||||
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`
|
||||
operator and test away. For example:
|
||||
|
||||
|
||||
Controller Function:
|
||||
<pre>
|
||||
function myController() {
|
||||
|
|
@ -274,31 +219,25 @@ function myController() {
|
|||
{"name":"jalapeno", "spiceiness":"hot hot hot!"},
|
||||
{"name":"habanero", "spiceness":"LAVA HOT!!"}];
|
||||
|
||||
|
||||
this.spice = "habanero";
|
||||
}
|
||||
</pre>
|
||||
|
||||
|
||||
Controller Test:
|
||||
<pre>
|
||||
describe('myController function', function() {
|
||||
|
||||
|
||||
describe('myController', function(){
|
||||
var ctrl;
|
||||
|
||||
|
||||
beforeEach(function() {
|
||||
ctrl = new myController();
|
||||
});
|
||||
|
||||
|
||||
it('should create "spices" model with 3 spices', function() {
|
||||
expect(ctrl.spices.length).toBe(3);
|
||||
});
|
||||
|
||||
|
||||
it('should set the default value of spice', function() {
|
||||
expect(ctrl.spice).toBe('habanero');
|
||||
});
|
||||
|
|
@ -306,18 +245,16 @@ describe('myController function', function() {
|
|||
});
|
||||
</pre>
|
||||
|
||||
|
||||
- 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.
|
||||
- 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.
|
||||
|
||||
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
|
||||
* {@link dev_guide.mvc About MVC in Angular}
|
||||
* {@link dev_guide.mvc.understanding_model Understanding the Model Component}
|
||||
* {@link dev_guide.mvc.understanding_view Understanding the View Component}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -3,94 +3,70 @@
|
|||
@name Developer Guide: About MVC in Angular: Understanding the Model Component
|
||||
@description
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
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).
|
||||
|
||||
|
||||
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
|
||||
created explicitly or implicitly.
|
||||
|
||||
|
||||
You can create models by explicitly creating scope properties referencing JavaScript objects in the
|
||||
following ways:
|
||||
|
||||
|
||||
* Make a direct property assignment to the scope object in JavaScript code; this most commonly
|
||||
occurs in controllers:
|
||||
|
||||
|
||||
function MyCtrl() {
|
||||
// create property 'foo' on the MyCtrl's scope
|
||||
// and assign it an initial value 'bar'
|
||||
this.foo = 'bar';
|
||||
}
|
||||
|
||||
|
||||
* Use an {@link dev_guide.expressions angular expression} with an assignment operator in templates:
|
||||
|
||||
|
||||
<button ng:click="{{foos='ball'}}">Click me</button>
|
||||
|
||||
|
||||
* Use {@link api/angular.directive.ng:init ng:init directive} in templates (for toy/example apps
|
||||
only, not recommended for real applications):
|
||||
|
||||
|
||||
<body ng:init=" foo = 'bar' ">
|
||||
|
||||
|
||||
Angular creates models implicitly (by creating a scope property and assigning it a suitable value)
|
||||
when processing the following template constructs:
|
||||
|
||||
|
||||
* Form input, select, and textarea elements:
|
||||
|
||||
|
||||
<input name="query" value="fluffy cloud">
|
||||
|
||||
The code above creates a model called "query" on the current scope with the value set to "fluffy
|
||||
cloud".
|
||||
|
||||
|
||||
* An iterator declaration in {@link api/angular.widget.@ng:repeat ng:repeater}:
|
||||
|
||||
|
||||
<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"
|
||||
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:
|
||||
|
||||
|
||||
* 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
|
||||
garbage collection.
|
||||
|
||||
|
||||
The following illustration shows a simple data model created implicitly from a simple template:
|
||||
|
||||
|
||||
<img src="img/guide/about_model_final.png">
|
||||
|
||||
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
|
||||
* {@link dev_guide.mvc About MVC in Angular}
|
||||
* {@link dev_guide.mvc.understanding_controller Understanding the Controller Component}
|
||||
* {@link dev_guide.mvc.understanding_view Understanding the View Component}
|
||||
|
|
|
|||
|
|
@ -3,14 +3,11 @@
|
|||
@name Developer Guide: About MVC in Angular: Understanding the View Component
|
||||
@description
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
<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.
|
||||
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
|
||||
|
|
@ -19,11 +16,8 @@ ng:controller} and {@link api/angular.widget.ng:view ng:view}, and through bindi
|
|||
controller function.
|
||||
|
||||
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
|
||||
* {@link dev_guide.mvc About MVC in Angular}
|
||||
* {@link dev_guide.mvc.understanding_model Understanding the Model Component}
|
||||
* {@link dev_guide.mvc.understanding_controller Understanding the Controller Component}
|
||||
|
|
|
|||
|
|
@ -3,33 +3,25 @@
|
|||
@description
|
||||
|
||||
|
||||
|
||||
|
||||
# What Is Angular?
|
||||
|
||||
|
||||
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
|
||||
/ JavaScript home.
|
||||
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
|
||||
* 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.
|
||||
|
||||
|
||||
* 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
|
||||
new tricks.
|
||||
|
||||
|
||||
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
|
||||
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!
|
||||
|
||||
|
||||
|
||||
|
||||
## An Introductory Angular Example
|
||||
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
|
||||
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
|
||||
easier a web developer's life can if they're using angular:
|
||||
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<b>Invoice:</b>
|
||||
|
|
@ -85,43 +72,32 @@ ng:required/></td>
|
|||
-->
|
||||
</doc:example>
|
||||
|
||||
|
||||
Try out the Live Preview above, and then let's walk through the example and describe what's going
|
||||
on.
|
||||
|
||||
|
||||
In the `<html>` tag, we add an attribute to let the browser know about the angular namespace:
|
||||
|
||||
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
|
||||
|
||||
This ensures angular runs nicely in all major browsers.
|
||||
|
||||
|
||||
In the `<script>` tag we do two angular setup tasks:
|
||||
|
||||
|
||||
1. We load `angular.js`.
|
||||
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.
|
||||
|
||||
|
||||
`<script src="http://code.angularjs.org/0.9.15/angular-0.9.15.min.js"
|
||||
ng:autobind></script>`
|
||||
|
||||
|
||||
From the `name` attribute of the `<input>` tags, angular automatically sets up two-way data
|
||||
binding, and we also demonstrate some easy input validation:
|
||||
|
||||
|
||||
Quantity: <input name="qty" value="1" ng:validate="integer:0" ng:required/>
|
||||
Cost: <input name="cost" value="199.95" ng:validate="number" ng:required/>
|
||||
|
||||
|
||||
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
|
||||
variables of the same name. Think of those variables as the "Model" component of the
|
||||
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,
|
||||
no? Yes.
|
||||
|
||||
|
||||
And finally, the mysterious `{{ double curly braces }}`:
|
||||
|
||||
|
||||
Total: {{qty * cost | currency}}
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
formatting display data.
|
||||
|
||||
|
||||
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
|
||||
into output that looks like money."
|
||||
|
||||
|
||||
|
||||
|
||||
# The Angular Philosophy
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
could do so by simply adding text to the HTML template, saving the code, and refreshing your
|
||||
browser:
|
||||
|
||||
|
||||
<pre>
|
||||
<span class="label">Hello</span>
|
||||
</pre>
|
||||
|
||||
|
||||
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:
|
||||
|
||||
|
||||
<pre>
|
||||
var label = new Label();
|
||||
label.setText('Hello');
|
||||
|
|
@ -182,15 +147,11 @@ label.setClass('label');
|
|||
parent.addChild(label);
|
||||
</pre>
|
||||
|
||||
|
||||
That's one line of markup versus four times as much code.
|
||||
|
||||
|
||||
|
||||
|
||||
## More Angular Philosophy
|
||||
|
||||
|
||||
* It is a very good idea to decouple DOM manipulation from app logic. This dramatically improves
|
||||
the testability of the code.
|
||||
* 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.
|
||||
* 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
|
||||
that angular is not:
|
||||
|
||||
|
||||
* It's not a Library. You don't just call its functions, although it does provide you with some
|
||||
utility APIs.
|
||||
* 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.
|
||||
|
||||
|
||||
|
||||
|
||||
# Why You Want Angular
|
||||
|
||||
|
||||
Angular frees you from the following pain:
|
||||
|
||||
|
||||
* **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
|
||||
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.
|
||||
|
||||
|
||||
|
||||
|
||||
# Watch a Presentation About Angular
|
||||
|
||||
|
||||
Here is an early presentation on angular, but note that substantial development has occurred since
|
||||
the talk was given in July of 2010.
|
||||
|
||||
|
||||
<object width="480" height="385">
|
||||
<param name="movie" value="http://www.youtube.com/v/elvcgVSynRg&hl=en_US&fs=1"></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>
|
||||
</object>
|
||||
|
||||
|
||||
{@link
|
||||
|
||||
https://docs.google.com/present/edit?id=0Abz6S2TvsDWSZDQ0OWdjaF8yNTRnODczazdmZg&hl=en&authkey=CO-b7oID
|
||||
|
|
|
|||
|
|
@ -3,48 +3,37 @@
|
|||
@name Developer Guide: Scopes: Applying Controllers to Scopes
|
||||
@description
|
||||
|
||||
|
||||
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:
|
||||
|
||||
|
||||
<pre>
|
||||
var scope = angular.scope();
|
||||
scope.salutation = 'Hello';
|
||||
scope.name = 'World';
|
||||
|
||||
|
||||
expect(scope.greeting).toEqual(undefined);
|
||||
|
||||
|
||||
scope.$watch('name', function(){
|
||||
this.greeting = this.salutation + ' ' + this.name + '!';
|
||||
});
|
||||
|
||||
|
||||
expect(scope.greeting).toEqual('Hello World!');
|
||||
scope.name = 'Misko';
|
||||
// scope.$eval() will propagate the change to listeners
|
||||
expect(scope.greeting).toEqual('Hello World!');
|
||||
|
||||
|
||||
scope.$eval();
|
||||
expect(scope.greeting).toEqual('Hello Misko!');
|
||||
</pre>
|
||||
|
||||
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
|
||||
* {@link dev_guide.scopes Angular Scope Objects}
|
||||
* {@link dev_guide.scopes.understanding_scopes Understanding Angular Scopes}
|
||||
* {@link dev_guide.scopes.working_scopes Working With Angular Scopes}
|
||||
* {@link dev_guide.scopes.updating_scopes Updating Angular Scopes}
|
||||
|
||||
|
||||
## Related API
|
||||
|
||||
|
||||
* {@link api/angular.scope Angular Scope API}
|
||||
|
|
|
|||
|
|
@ -4,48 +4,35 @@
|
|||
@description
|
||||
|
||||
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
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
|
||||
can think of angular scope objects as the medium through which the model, view, and controller
|
||||
communicate.
|
||||
|
||||
|
||||
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
|
||||
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).
|
||||
|
||||
|
||||
Angular scope objects are responsible for:
|
||||
|
||||
|
||||
* Gluing the model, controller and view template together.
|
||||
* 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}).
|
||||
* Providing the context in which all controller functions and angular expressions are evaluated.
|
||||
|
||||
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
|
||||
* {@link dev_guide.scopes.understanding_scopes Understanding Scopes}
|
||||
* {@link dev_guide.scopes.working_scopes Working With Scopes}
|
||||
* {@link dev_guide.scopes.controlling_scopes Applying Controllers to Scopes}
|
||||
* {@link dev_guide.scopes.updating_scopes Updating Scopes}
|
||||
|
||||
|
||||
## Related API
|
||||
|
||||
|
||||
* {@link api/angular.scope Angular Scope API}
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
@name Developer Guide: Scopes: Understanding Scopes
|
||||
@description
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
functions you have created to their respective scope objects.
|
||||
|
||||
|
||||
<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
|
||||
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
|
||||
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
|
||||
code, observe how the value of `name` changes, based on the HTML element it is displayed in:
|
||||
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<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)).
|
||||
toEqual(['Misko']);
|
||||
|
||||
|
||||
expect(using('.doc-example-live').repeater('li').row(2)).
|
||||
toEqual(['Gail']);
|
||||
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:example>
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
original value of `name` is displayed.
|
||||
|
||||
|
||||
The following illustration shows the DOM and angular scopes for the example above:
|
||||
|
||||
|
||||
<img src="img/guide/dom_scope_final.png">
|
||||
|
||||
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
|
||||
* {@link dev_guide.scopes Angular Scope Objects}
|
||||
* {@link dev_guide.scopes.working_scopes Working With Scopes}
|
||||
* {@link dev_guide.scopes.controlling_scopes Applying Controllers to Scopes}
|
||||
* {@link dev_guide.scopes.updating_scopes Updating Scopes}
|
||||
|
||||
|
||||
## Related API
|
||||
|
||||
|
||||
* {@link api/angular.scope Angular Scope API}
|
||||
|
|
|
|||
|
|
@ -3,19 +3,16 @@
|
|||
@name Developer Guide: Scopes: Updating Scope Properties
|
||||
@description
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
your own custom widget or service.
|
||||
|
||||
|
||||
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
|
||||
`$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
|
||||
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
|
||||
|
|
@ -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
|
||||
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
|
||||
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
|
||||
for your app; the bound views update immediately as the user enters data.
|
||||
|
||||
|
||||
|
||||
|
||||
## Related Documents
|
||||
|
||||
|
||||
* {@link dev_guide.scopes 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.controlling_scopes Applying Controllers to Scopes}
|
||||
|
||||
|
||||
## Related API
|
||||
|
||||
|
||||
* {@link api/angular.scope Angular Scope API}
|
||||
|
|
|
|||
|
|
@ -3,27 +3,22 @@
|
|||
@name Developer Guide: Scopes: Working With Angular Scopes
|
||||
@description
|
||||
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
|
||||
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:
|
||||
|
||||
|
||||
<pre>
|
||||
var scope = angular.scope();
|
||||
scope.salutation = 'Hello';
|
||||
scope.name = 'World';
|
||||
|
||||
|
||||
// Verify that greeting is undefined
|
||||
expect(scope.greeting).toEqual(undefined);
|
||||
|
||||
|
||||
// Set up the watcher...
|
||||
scope.$watch('name', function(){
|
||||
// when 'name' changes, set 'greeting'...
|
||||
|
|
@ -31,35 +26,27 @@ this.greeting = this.salutation + ' ' + this.name + '!';
|
|||
}
|
||||
);
|
||||
|
||||
|
||||
// verify that 'greeting' was set...
|
||||
expect(scope.greeting).toEqual('Hello World!');
|
||||
|
||||
|
||||
// 'name' changed!
|
||||
scope.name = 'Misko';
|
||||
|
||||
|
||||
// scope.$eval() will propagate the change to listeners
|
||||
expect(scope.greeting).toEqual('Hello World!');
|
||||
|
||||
|
||||
scope.$eval();
|
||||
// verify that '$eval' propagated the change
|
||||
expect(scope.greeting).toEqual('Hello Misko!');
|
||||
</pre>
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
|
||||
* {@link dev_guide.scopes Angular Scope Objects}
|
||||
* {@link dev_guide.scopes.understanding_scopes Understanding Scopes}
|
||||
* {@link dev_guide.scopes.controlling_scopes Applying Controllers to Scopes}
|
||||
* {@link dev_guide.scopes.updating_scopes Updating Scopes}
|
||||
|
||||
|
||||
## Related API
|
||||
|
||||
|
||||
* {@link api/angular.scope Angular Scope API}
|
||||
|
|
|
|||
|
|
@ -3,15 +3,12 @@
|
|||
@name Developer Guide: Angular Services: Creating Angular Services
|
||||
@description
|
||||
|
||||
|
||||
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
|
||||
that angular's DI will use to create the service object when it is needed.
|
||||
|
||||
|
||||
The `angular.service` method accepts three parameters:
|
||||
|
||||
|
||||
- `{string} name` - Name of the service.
|
||||
- `{function()} factory` - Factory function (called just once by DI).
|
||||
- `{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
|
||||
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.
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
testable.
|
||||
|
||||
|
||||
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
|
||||
stores all notifications; after the third one, the service displays all of the notifications by
|
||||
window alert.
|
||||
|
||||
|
||||
<pre>
|
||||
angular.service('notify', function(win) {
|
||||
var msgs = [];
|
||||
|
|
@ -53,19 +46,14 @@ window alert.
|
|||
</pre>
|
||||
|
||||
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
|
||||
* {@link dev_guide.services.understanding_services Understanding Angular Services}
|
||||
* {@link dev_guide.services.registering_services Registering Angular Services}
|
||||
* {@link dev_guide.services.managing_dependencies Managing Service Dependencies}
|
||||
* {@link dev_guide.services.injecting_controllers Injecting Services Into Controllers }
|
||||
* {@link dev_guide.services.testing_services Testing Angular Services}
|
||||
|
||||
|
||||
## Related API
|
||||
|
||||
|
||||
* {@link api/angular.service Angular Service API}
|
||||
|
|
|
|||
|
|
@ -3,11 +3,9 @@
|
|||
@name Developer Guide: Angular Services: Injecting Services Into Controllers
|
||||
@description
|
||||
|
||||
|
||||
Using services as dependencies for controllers is very similar to using services as dependencies
|
||||
for another service.
|
||||
|
||||
|
||||
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
|
||||
`$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
|
||||
convention they match the service IDs.
|
||||
|
||||
|
||||
<pre>
|
||||
function myController($loc, $log) {
|
||||
this.firstMethod = function() {
|
||||
|
|
@ -32,7 +29,6 @@ this.secondMethod = function() {
|
|||
myController.$inject = ['$location', '$log'];
|
||||
</pre>
|
||||
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script type="text/javascript">
|
||||
|
|
@ -47,18 +43,15 @@ angular.service('notify', function(win) {
|
|||
};
|
||||
}, {$inject: ['$window']});
|
||||
|
||||
|
||||
function myController(notifyService) {
|
||||
this.callNotify = function(msg) {
|
||||
notifyService(msg);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
myController.$inject = ['notify'];
|
||||
</script>
|
||||
|
||||
|
||||
<div ng:controller="myController">
|
||||
<p>Let's try this simple notify service, injected into the controller...</p>
|
||||
<input ng:init="message='test'" type="text" name="message" />
|
||||
|
|
@ -73,19 +66,14 @@ it('should test service', function(){
|
|||
</doc:example>
|
||||
|
||||
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
|
||||
{@link dev_guide.services.understanding_services Understanding Angular Services}
|
||||
{@link dev_guide.services.creating_services Creating Angular Services}
|
||||
{@link dev_guide.services.registering_services Registering Angular Services}
|
||||
{@link dev_guide.services.managing_dependencies Managing Service Dependencies}
|
||||
{@link dev_guide.services.testing_services Testing Angular Services}
|
||||
|
||||
|
||||
## Related API
|
||||
|
||||
|
||||
{@link api/angular.service Angular Service API}
|
||||
|
|
|
|||
|
|
@ -3,20 +3,16 @@
|
|||
@name Developer Guide: Angular Services: Managing Service Dependencies
|
||||
@description
|
||||
|
||||
|
||||
Angular allows services to declare other services as dependencies needed for construction of their
|
||||
instances.
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
provided by angular's web framework:
|
||||
|
||||
|
||||
<pre>
|
||||
/**
|
||||
* 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) {
|
||||
var messageQueue = [];
|
||||
|
||||
|
||||
function log() {
|
||||
if (messageQueue.length) {
|
||||
$log('batchLog messages: ', messageQueue);
|
||||
|
|
@ -36,14 +31,12 @@ function log() {
|
|||
$defer(log, 50000);
|
||||
}
|
||||
|
||||
|
||||
return function(message) {
|
||||
messageQueue.push(message);
|
||||
}
|
||||
}, {$inject: ['$defer', '$log']);
|
||||
// note how we declared dependency on built-in $defer and $log services above
|
||||
|
||||
|
||||
/**
|
||||
* routeTemplateMonitor monitors each $route change and logs the current
|
||||
* template via the batchLog service.
|
||||
|
|
@ -55,10 +48,8 @@ $route.onChange(function() {
|
|||
}, {$inject: ['$route', 'batchLog'], $eager: true});
|
||||
</pre>
|
||||
|
||||
|
||||
Things to notice in this example:
|
||||
|
||||
|
||||
* 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
|
||||
`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.
|
||||
|
||||
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
|
||||
* {@link dev_guide.services.understanding_services Understanding Angular Services}
|
||||
* {@link dev_guide.services.creating_services Creating Angular Services}
|
||||
* {@link dev_guide.services.registering_services Registering Services}
|
||||
* {@link dev_guide.services.injecting_controllers Injecting Services Into Controllers }
|
||||
* {@link dev_guide.services.testing_services Testing Angular Services}
|
||||
|
||||
|
||||
## Related API
|
||||
|
||||
|
||||
* {@link api/angular.service Angular Service API}
|
||||
* {@link api/angular.injector Angular Injector API}
|
||||
|
|
|
|||
|
|
@ -3,18 +3,14 @@
|
|||
@name Developer Guide: Angular Services
|
||||
@description
|
||||
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
|
||||
* {@link dev_guide.services.understanding_services Understanding Angular Services}
|
||||
* {@link dev_guide.services.creating_services Creating 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.testing_services Testing Angular Services}
|
||||
|
||||
|
||||
## Related API
|
||||
|
||||
|
||||
* {@link api/angular.service Angular Service API}
|
||||
|
|
|
|||
|
|
@ -3,12 +3,10 @@
|
|||
@name Developer Guide: Angular Services: Registering Angular Services
|
||||
@description
|
||||
|
||||
|
||||
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
|
||||
following pseudo-code shows a simple service registration:
|
||||
|
||||
|
||||
<pre>
|
||||
angular.service('service id', function() {
|
||||
var shinyNewServiceInstance;
|
||||
|
|
@ -17,27 +15,21 @@ angular.service('service id', function() {
|
|||
});
|
||||
</pre>
|
||||
|
||||
|
||||
Note that you are not registering a service instance, but rather a factory function that will
|
||||
create this instance when called.
|
||||
|
||||
|
||||
# Instantiating Angular Services
|
||||
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
|
||||
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}.
|
||||
|
||||
|
||||
To override the default, you can request that a service is eagerly instantiated as follows:
|
||||
|
||||
|
||||
<pre>
|
||||
angular.service('service id', function() {
|
||||
var shinyNewServiceInstance;
|
||||
|
|
@ -46,12 +38,10 @@ angular.service('service id', function() {
|
|||
}, {$eager: true});
|
||||
</pre>
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
state or configuration of the application (e.g. a service that configures `$route` or `$resource`
|
||||
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
|
||||
application memory usage.
|
||||
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
|
||||
* {@link dev_guide.services.understanding_services Understanding Angular Services}
|
||||
* {@link dev_guide.services.creating_services Creating Angular Services}
|
||||
* {@link dev_guide.services.managing_dependencies Managing Service Dependencies}
|
||||
* {@link dev_guide.services.injecting_controllers Injecting Services Into Controllers }
|
||||
* {@link dev_guide.services.testing_services Testing Angular Services}
|
||||
|
||||
|
||||
## Related API
|
||||
|
||||
|
||||
* {@link api/angular.service Angular Service API}
|
||||
|
|
|
|||
|
|
@ -3,29 +3,24 @@
|
|||
@name Developer Guide: Angular Services: Testing Angular Services
|
||||
@description
|
||||
|
||||
|
||||
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
|
||||
Jasmine spy (mock) instead of a real browser alert.
|
||||
|
||||
|
||||
<pre>
|
||||
var mock, notify;
|
||||
|
||||
|
||||
beforeEach(function() {
|
||||
mock = {alert: jasmine.createSpy()};
|
||||
notify = angular.service('notify')(mock);
|
||||
});
|
||||
|
||||
|
||||
it('should not alert first two notifications', function() {
|
||||
notify('one');
|
||||
notify('two');
|
||||
expect(mock.alert).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
||||
it('should alert all after third notification', function() {
|
||||
notify('one');
|
||||
notify('two');
|
||||
|
|
@ -33,7 +28,6 @@ notify('three');
|
|||
expect(mock.alert).toHaveBeenCalledWith("one\ntwo\nthree");
|
||||
});
|
||||
|
||||
|
||||
it('should clear messages after alert', function() {
|
||||
notify('one');
|
||||
notify('two');
|
||||
|
|
@ -47,24 +41,16 @@ expect(mock.alert.mostRecentCall.args).toEqual(["more\ntwo\nthird"]);
|
|||
</pre>
|
||||
|
||||
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
|
||||
* {@link dev_guide.services.understanding_services Understanding Angular Services}
|
||||
* {@link dev_guide.services.creating_services Creating Angular Services}
|
||||
* {@link dev_guide.services.registering_services Registering Angular Services}
|
||||
* {@link dev_guide.services.managing_dependencies Managing Service Dependencies}
|
||||
* {@link dev_guide.services.injecting_controllers Injecting Services Into Conrollers}
|
||||
|
||||
|
||||
## Related API
|
||||
|
||||
|
||||
* {@link api/angular.service Angular Service API}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -3,46 +3,36 @@
|
|||
@name Developer Guide: Angular Services: Understanding Angular Services
|
||||
@description
|
||||
|
||||
|
||||
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
|
||||
`XMLHttpRequest` object.
|
||||
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
|
||||
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
|
||||
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:
|
||||
|
||||
|
||||
myController.$inject = ['$location'];
|
||||
|
||||
|
||||
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
|
||||
above). You can also create your own custom services.
|
||||
|
||||
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
|
||||
* {@link dev_guide.di About Angular Dependency Injection}
|
||||
* {@link dev_guide.services.creating_services Creating Angular Services}
|
||||
* {@link dev_guide.services.registering_services Registering Angular Services}
|
||||
* {@link dev_guide.services.managing_dependencies Managing Service Dependencies}
|
||||
* {@link dev_guide.services.testing_services Testing Angular Services}
|
||||
|
||||
|
||||
## Related API
|
||||
|
||||
|
||||
* {@link api/angular.service Angular Service API}
|
||||
* {@link api/angular.injector Injector API}
|
||||
|
|
|
|||
|
|
@ -4,66 +4,48 @@
|
|||
@description
|
||||
|
||||
|
||||
|
||||
|
||||
Angular includes built-in CSS classes, which in turn have predefined CSS styles.
|
||||
|
||||
|
||||
# Built-in CSS classes
|
||||
|
||||
|
||||
* `ng-exception`
|
||||
|
||||
|
||||
**Usage:** angular applies this class to a DOM element if that element contains an Expression that
|
||||
threw an exception when evaluated.
|
||||
|
||||
|
||||
**Styling:** The built-in styling of the ng-exception class displays an error message surrounded
|
||||
by a solid red border, for example:
|
||||
|
||||
|
||||
<div class="ng-exception">Error message</div>
|
||||
|
||||
|
||||
You can try to evaluate malformed expressions in {@link dev_guide.expressions expressions} to see
|
||||
the `ng-exception` class' styling.
|
||||
|
||||
|
||||
* `ng-validation-error`
|
||||
|
||||
|
||||
**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
|
||||
Ng:validate or Ng:required directives.
|
||||
|
||||
|
||||
**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
|
||||
can see an example in {@link api/angular.widget.@ng:validate ng:validate example}.
|
||||
|
||||
|
||||
## Overriding Styles for Angular CSS Classes
|
||||
|
||||
|
||||
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.
|
||||
* Create a local CSS file, overriding any styles that you'd like, and link to it from your HTML file
|
||||
as you normally would:
|
||||
|
||||
|
||||
<pre>
|
||||
<link href="yourfile.css" rel="stylesheet" type="text/css">
|
||||
</pre>
|
||||
|
||||
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
|
||||
* {@link dev_guide.templates Angular Templates}
|
||||
* {@link dev_guide.templates.css Working With CSS in Angular}
|
||||
* {@link dev_guide.templates.formatters Angular Formatters}
|
||||
|
|
|
|||
|
|
@ -3,16 +3,13 @@
|
|||
@name Developer Guide: Templates: Data Binding in Angular
|
||||
@description
|
||||
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
|
||||
## Data Binding in Classical Template Systems
|
||||
|
||||
|
||||
<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
|
||||
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
|
||||
to write code that constantly syncs the view with the model and the model with the view.
|
||||
|
||||
|
||||
## Data Binding in Angular Templates
|
||||
|
||||
|
||||
<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
|
||||
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 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
|
||||
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.
|
||||
|
||||
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
|
||||
* {@link dev_guide.scopes Angular Scopes}
|
||||
* {@link dev_guide.templates Angular Templates}
|
||||
|
|
|
|||
|
|
@ -3,25 +3,20 @@
|
|||
@name Developer Guide: Templates: Filters: Creating Angular Filters
|
||||
@description
|
||||
|
||||
|
||||
Writing your own filter is very easy: just define a JavaScript function on the `angular.filter`
|
||||
object.
|
||||
The framework passes in the input value as the first argument to your function. Any filter
|
||||
arguments are passed in as additional function arguments.
|
||||
|
||||
|
||||
You can use these variables in the function:
|
||||
|
||||
|
||||
* `this` — The current scope.
|
||||
* `this.$element` — The DOM element containing the binding. The `$element` variable allows the
|
||||
filter to manipulate the DOM.
|
||||
|
||||
|
||||
The following sample filter reverses a text string. In addition, it conditionally makes the
|
||||
text upper-case and assigns color.
|
||||
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script type="text/javascript">
|
||||
|
|
@ -42,7 +37,6 @@ text upper-case and assigns color.
|
|||
});
|
||||
</script>
|
||||
|
||||
|
||||
<input name="text" type="text" value="hello" /><br>
|
||||
No filter: {{text}}<br>
|
||||
Reverse: {{text|reverse}}<br>
|
||||
|
|
@ -59,16 +53,11 @@ expect(binding('text|reverse')).toEqual('CBA');
|
|||
</doc:example>
|
||||
|
||||
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
|
||||
* {@link dev_guide.templates.filters Understanding Angular Filters}
|
||||
* {@link dev_guide.compiler Angular HTML Compiler}
|
||||
|
||||
|
||||
## Related API
|
||||
|
||||
|
||||
* {@link api/angular.filter Angular Filter API}
|
||||
|
|
|
|||
|
|
@ -3,36 +3,26 @@
|
|||
@name Developer Guide: Templates: Understanding Angular Filters
|
||||
@description
|
||||
|
||||
|
||||
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
|
||||
to filtered output.
|
||||
|
||||
|
||||
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:
|
||||
|
||||
|
||||
name | 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
|
||||
tasks such as conditionally applying CSS styles to filtered output.
|
||||
|
||||
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
|
||||
* {@link dev_guide.templates.filters.using_filters Using Angular Filters}
|
||||
* {@link dev_guide.templates.filters.creating_filters Creating Angular Filters}
|
||||
|
||||
|
||||
## Related API
|
||||
|
||||
|
||||
* {@link api/angular.filter Angular Filter API}
|
||||
|
|
|
|||
|
|
@ -3,35 +3,26 @@
|
|||
@name Developer Guide: Templates: Filters: Using Angular Filters
|
||||
@description
|
||||
|
||||
|
||||
Filters can be part of any {@link api/angular.scope} evaluation but are typically used to format
|
||||
expressions in bindings in your templates:
|
||||
|
||||
|
||||
{{ expression | filter }}
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
You can chain filters using this syntax:
|
||||
|
||||
|
||||
{{ expression | filter1 | filter2 }}
|
||||
|
||||
|
||||
You can also pass colon-delimited arguments to filters, for example, to display the number 123 with
|
||||
2 decimal points:
|
||||
|
||||
|
||||
123 | number:2
|
||||
|
||||
|
||||
Here are some examples that show values before and after applying different filters to an
|
||||
expression in a binding:
|
||||
|
||||
|
||||
* No filter: `{{1234.5678}}` => `1234.5678`
|
||||
* Number filter: `{{1234.5678|number}}` => `1,234.57`. Notice the "," and rounding to two
|
||||
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.
|
||||
|
||||
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
|
||||
* {@link dev_guide.templates.filters Understanding Angular Filters}
|
||||
* {@link dev_guide.templates.filters.creating_filters Creating Angular Filters}
|
||||
|
||||
|
||||
## Related API
|
||||
|
||||
|
||||
* {@link api/angular.filter Angular Filter API}
|
||||
|
|
|
|||
|
|
@ -3,19 +3,16 @@
|
|||
@name Developer Guide: Templates: Angular Formatters: Creating Angular Formatters
|
||||
@description
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
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 data changes in the model, it is automatically formatted to the user-readable form for display
|
||||
in the view.
|
||||
|
||||
|
||||
<pre>
|
||||
function reverse(text) {
|
||||
var reversed = [];
|
||||
|
|
@ -25,7 +22,6 @@ reversed.unshift(text.charAt(i));
|
|||
return reversed.join('');
|
||||
}
|
||||
|
||||
|
||||
angular.formatter('reverse', {
|
||||
parse: function(value){
|
||||
return reverse(value||'').toUpperCase();
|
||||
|
|
@ -36,7 +32,6 @@ return reverse(value||'').toLowerCase();
|
|||
});
|
||||
</pre>
|
||||
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script type="text/javascript">
|
||||
|
|
@ -48,7 +43,6 @@ for (var i = 0; i < text.length; i++) {
|
|||
return reversed.join('');
|
||||
}
|
||||
|
||||
|
||||
angular.formatter('reverse', {
|
||||
parse: function(value){
|
||||
return reverse(value||'').toUpperCase();
|
||||
|
|
@ -59,5 +53,3 @@ format: function(value){
|
|||
});
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -3,24 +3,18 @@
|
|||
@name Developer Guide: Templates: Angular Formatters
|
||||
@description
|
||||
|
||||
|
||||
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
|
||||
can manipulate.
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
|
||||
* {@link dev_guide.templates.formatters.using_formatters Using Angular Formatters}
|
||||
* {@link dev_guide.templates.formatters.creating_formatters Creating Angular Formatters}
|
||||
|
||||
|
||||
## Related API
|
||||
|
||||
|
||||
* {@link api/angular.formatter Angular Formatter API}
|
||||
|
|
|
|||
|
|
@ -3,10 +3,7 @@
|
|||
@name Developer Guide: Templates: Angular Formatters: Using Angular Formatters
|
||||
@description
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
<pre>
|
||||
|
||||
|
|
|
|||
|
|
@ -3,17 +3,14 @@
|
|||
@name Developer Guide: Understanding Angular Templates
|
||||
@description
|
||||
|
||||
|
||||
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,
|
||||
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
|
||||
the dynamic view DOM.
|
||||
|
||||
|
||||
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
|
||||
element.
|
||||
* {@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
|
||||
readable view.
|
||||
|
||||
|
||||
Note: In addition to declaring the elements above in templates, you can also access these elements
|
||||
in JavaScript code.
|
||||
|
||||
|
||||
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},
|
||||
and {@link dev_guide.expressions expressions}:
|
||||
|
||||
|
||||
<pre>
|
||||
<html>
|
||||
<!-- Body tag augmented with ng:controller directive -->
|
||||
|
|
@ -49,7 +43,6 @@ and {@link dev_guide.expressions expressions}:
|
|||
</html>
|
||||
</pre>
|
||||
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
|
||||
* {@link dev_guide.templates.filters Angular Filters}
|
||||
* {@link dev_guide.templates.formatters Angular Formatters}
|
||||
* {@link dev_guide.templates.validators Angular Validators}
|
||||
|
||||
|
||||
## Related API
|
||||
|
||||
|
||||
* {@link api/index API Reference}
|
||||
|
|
|
|||
|
|
@ -4,17 +4,13 @@
|
|||
@description
|
||||
|
||||
|
||||
|
||||
|
||||
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
|
||||
treated as an argument to the validator function. Any additional inputs should be separated by
|
||||
commas.
|
||||
|
||||
|
||||
The following bit of pseudo-code shows how to set up a custom validator:
|
||||
|
||||
|
||||
<pre>
|
||||
angular.validator('your_validator', function(input [,additional params]) {
|
||||
[your validation code];
|
||||
|
|
@ -26,22 +22,17 @@ angular.validator('your_validator', function(input [,additional params]) {
|
|||
}
|
||||
</pre>
|
||||
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
|
||||
To specify an error message, replace "`return true;`" with an error string, for example:
|
||||
|
||||
|
||||
return "Must be a value between 1 and 5!";
|
||||
|
||||
|
||||
Following is a sample UPS Tracking Number validator:
|
||||
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script>
|
||||
|
|
@ -60,7 +51,6 @@ expect(element('input[name=trackNo]').attr('class')).
|
|||
not().toMatch(/ng-validation-error/);
|
||||
});
|
||||
|
||||
|
||||
it('should not validate in correct UPS tracking number', function() {
|
||||
input('trackNo').enter('foo');
|
||||
expect(element('input[name=trackNo]').attr('class')).
|
||||
|
|
@ -69,32 +59,24 @@ expect(element('input[name=trackNo]').attr('class')).
|
|||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
|
||||
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
|
||||
returns the specified error message ("true").
|
||||
|
||||
|
||||
Note: you can also access the current angular scope and DOM element objects in your validator
|
||||
functions as follows:
|
||||
|
||||
|
||||
* `this` === The current angular scope.
|
||||
* `this.$element` === The DOM element that contains the binding. This allows the filter to
|
||||
manipulate the DOM in addition to transforming the input.
|
||||
|
||||
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
|
||||
* {@link dev_guide.templates Angular Templates}
|
||||
* {@link dev_guide.templates.filters Angular Filters}
|
||||
* {@link dev_guide.templates.formatters Angular Formatters}
|
||||
|
||||
|
||||
## Related API
|
||||
|
||||
|
||||
* {@link api/angular.validator API Validator Reference}
|
||||
|
|
|
|||
|
|
@ -3,11 +3,9 @@
|
|||
@name Developer Guide: Templates: Understanding Angular Validators
|
||||
@description
|
||||
|
||||
|
||||
Angular validators are attributes that test the validity of different types of user input. Angular
|
||||
provides a set of built-in input validators:
|
||||
|
||||
|
||||
* {@link api/angular.validator.phone phone number}
|
||||
* {@link api/angular.validator.number number}
|
||||
* {@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.asynchronous asynchronous}
|
||||
|
||||
|
||||
You can also create your own custom validators.
|
||||
|
||||
|
||||
# Using Angular Validators
|
||||
|
||||
|
||||
You can use angular validators in HTML template bindings, and in JavaScript:
|
||||
|
||||
|
||||
* Validators in HTML Template Bindings
|
||||
|
||||
|
||||
<pre>
|
||||
<input ng:validator="validator_type:parameters" [...]>
|
||||
</pre>
|
||||
|
||||
|
||||
* Validators in JavaScript
|
||||
|
||||
|
||||
<pre>
|
||||
angular.validator.[validator_type](parameters)
|
||||
</pre>
|
||||
|
||||
|
||||
The following example shows how to use the built-in angular integer validator:
|
||||
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
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:example>
|
||||
|
||||
|
||||
# Creating an Angular Validator
|
||||
|
||||
|
||||
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
|
||||
treated as an argument to the validator function. Any additional inputs should be separated by
|
||||
commas.
|
||||
|
||||
|
||||
The following bit of pseudo-code shows how to set up a custom validator:
|
||||
|
||||
|
||||
<pre>
|
||||
angular.validator('your_validator', function(input [,additional params]) {
|
||||
[your validation code];
|
||||
|
|
@ -88,22 +73,17 @@ angular.validator('your_validator', function(input [,additional params]) {
|
|||
}
|
||||
</pre>
|
||||
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
|
||||
To specify an error message, replace "`return true;`" with an error string, for example:
|
||||
|
||||
|
||||
return "Must be a value between 1 and 5!";
|
||||
|
||||
|
||||
Following is a sample UPS Tracking Number validator:
|
||||
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script>
|
||||
|
|
@ -122,7 +102,6 @@ it('should validate correct UPS tracking number', function() {
|
|||
not().toMatch(/ng-validation-error/);
|
||||
});
|
||||
|
||||
|
||||
it('should not validate in correct UPS tracking number', function() {
|
||||
input('trackNo').enter('foo');
|
||||
expect(element('input[name=trackNo]').attr('class')).
|
||||
|
|
@ -131,30 +110,22 @@ it('should not validate in correct UPS tracking number', function() {
|
|||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
|
||||
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
|
||||
returns the specified error message ("true").
|
||||
|
||||
|
||||
Note: you can also access the current angular scope and DOM element objects in your validator
|
||||
functions as follows:
|
||||
|
||||
|
||||
* `this` === The current angular scope.
|
||||
* `this.$element` === The DOM element that contains the binding. This allows the filter to
|
||||
manipulate the DOM in addition to transforming the input.
|
||||
|
||||
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
|
||||
* {@link dev_guide.templates Angular Templates}
|
||||
|
||||
|
||||
## Related API
|
||||
|
||||
|
||||
* {@link api/angular.validator Validator API}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
@name Developer Guide: Unit Testing
|
||||
@description
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
you don't follow these, you may very well end up with an untestable application.
|
||||
|
||||
|
||||
## Dependency Inject
|
||||
There are several ways in which you can get a hold of a dependency:
|
||||
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)
|
||||
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:
|
||||
|
||||
|
||||
### Using the `new` operator
|
||||
|
||||
|
||||
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
|
||||
trying to instantiate an `XHR` so that we can get some data from the server.
|
||||
|
||||
|
||||
<pre>
|
||||
function MyClass(){
|
||||
this.doWork = function(){
|
||||
|
|
@ -59,13 +53,11 @@ function MyClass(){
|
|||
}
|
||||
</pre>
|
||||
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
|
||||
The class above is hard to test since we have to resort to monkey patching:
|
||||
<pre>
|
||||
var oldXHR = XHR;
|
||||
|
|
@ -77,12 +69,9 @@ XHR = oldXHR; // if you forget this bad things will happen
|
|||
</pre>
|
||||
|
||||
|
||||
|
||||
|
||||
### Global look-up:
|
||||
Another way to approach the problem is look for the service in a well known location.
|
||||
|
||||
|
||||
<pre>
|
||||
function MyClass(){
|
||||
this.doWork = function(){
|
||||
|
|
@ -95,7 +84,6 @@ function MyClass(){
|
|||
}
|
||||
</pre>
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
State & Singletons}
|
||||
|
||||
|
||||
The class above is hard to test since we have to change global state:
|
||||
<pre>
|
||||
var oldXHR = glabal.xhr;
|
||||
|
|
@ -115,15 +102,11 @@ global.xhr = oldXHR; // if you forget this bad things will happen
|
|||
</pre>
|
||||
|
||||
|
||||
|
||||
|
||||
### Service Registry:
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
<pre>
|
||||
function MyClass() {
|
||||
var serviceRegistry = ????;
|
||||
|
|
@ -137,13 +120,11 @@ function MyClass() {
|
|||
}
|
||||
</pre>
|
||||
|
||||
|
||||
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
|
||||
* 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).
|
||||
|
||||
|
||||
The class above is hard to test since we have to change global state:
|
||||
<pre>
|
||||
var oldServiceLocator = glabal.serviceLocator;
|
||||
|
|
@ -155,12 +136,9 @@ glabal.serviceLocator = oldServiceLocator; // if you forget this bad things will
|
|||
</pre>
|
||||
|
||||
|
||||
|
||||
|
||||
### Passing in Dependencies:
|
||||
Lastly the dependency can be passed in.
|
||||
|
||||
|
||||
<pre>
|
||||
function MyClass(xhr) {
|
||||
this.doWork = function(){
|
||||
|
|
@ -227,11 +205,9 @@ function PasswordController(){
|
|||
}
|
||||
</pre>
|
||||
|
||||
|
||||
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:
|
||||
|
||||
|
||||
<pre>
|
||||
var input = $('<input type="text"/>');
|
||||
var span = $('<span>');
|
||||
|
|
@ -246,11 +222,9 @@ expect(span.text()).toEqual('weak');
|
|||
$('body').html('');
|
||||
</pre>
|
||||
|
||||
|
||||
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:
|
||||
|
||||
|
||||
<pre>
|
||||
function PasswordCntrl(){
|
||||
this.password = '';
|
||||
|
|
@ -267,10 +241,8 @@ function PasswordCntrl(){
|
|||
}
|
||||
</pre>
|
||||
|
||||
|
||||
and the tests is straight forward
|
||||
|
||||
|
||||
<pre>
|
||||
var pc = new PasswordController();
|
||||
pc.password('abc');
|
||||
|
|
@ -278,37 +250,29 @@ pc.grade();
|
|||
expect(span.strength).toEqual('weak');
|
||||
</pre>
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
|
||||
|
||||
## Filters
|
||||
{@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
|
||||
logic, further simplifying the application logic.
|
||||
|
||||
|
||||
<pre>
|
||||
angular.filter('length', function(text){
|
||||
return (''+(text||'')).length;
|
||||
});
|
||||
|
||||
|
||||
var length = angular.filter('length');
|
||||
expect(length(null)).toEqual(0);
|
||||
expect(length('abc')).toEqual(3);
|
||||
</pre>
|
||||
|
||||
|
||||
## Directives
|
||||
Directives in angular are responsible for updating the DOM when the state of the model changes.
|
||||
|
||||
|
||||
|
||||
|
||||
## Mocks
|
||||
oue
|
||||
## Global State Isolation
|
||||
|
|
|
|||
|
|
@ -3,78 +3,59 @@
|
|||
@name Developer Guide
|
||||
@description
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
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:
|
||||
|
||||
|
||||
* {@link misc/started Getting Started}
|
||||
* {@link tutorial/index Angular Tutorial}
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## {@link dev_guide.overview Overview of Angular}
|
||||
|
||||
|
||||
## {@link dev_guide.bootstrap Initializing Angular}
|
||||
|
||||
|
||||
* {@link dev_guide.bootstrap.auto_bootstrap Understanding Automatic Initialization}
|
||||
* {@link dev_guide.bootstrap.manual_bootstrap Understanding Manual Initialization}
|
||||
|
||||
|
||||
## {@link dev_guide.mvc About MVC in Angular}
|
||||
|
||||
|
||||
* {@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_view Understanding the View Component}
|
||||
|
||||
|
||||
## {@link dev_guide.scopes 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.controlling_scopes Applying Controllers to Scopes}
|
||||
* {@link dev_guide.scopes.updating_scopes Updating Scope Properties}
|
||||
|
||||
|
||||
## {@link dev_guide.compiler Angular HTML Compiler}
|
||||
|
||||
|
||||
* {@link dev_guide.compiler.directives Understanding Angular Directives}
|
||||
* {@link dev_guide.compiler.widgets Understanding Angular Widgets}
|
||||
* {@link dev_guide.compiler.directives_widgets Comparing Directives and Widgets}
|
||||
* {@link dev_guide.compiler.markup Understanding Angular Markup}
|
||||
|
||||
|
||||
## {@link dev_guide.templates Angular Templates}
|
||||
|
||||
|
||||
* {@link dev_guide.templates.filters Understanding Angular Filters}
|
||||
* {@link dev_guide.templates.formatters Understanding Angular Formatters}
|
||||
* {@link dev_guide.templates.validators Understanding Angular Validators}
|
||||
|
||||
|
||||
## {@link dev_guide.services Angular Services}
|
||||
|
||||
|
||||
* {@link dev_guide.services.understanding_services Understanding Angular Services}
|
||||
* {@link dev_guide.services.creating_services Creating Angular Services}
|
||||
* {@link dev_guide.services.registering_services Registering Angular Services}
|
||||
* {@link dev_guide.services.managing_dependencies Managing Service Dependencies}
|
||||
* {@link dev_guide.services.testing_services Testing Angular Services}
|
||||
|
||||
|
||||
## {@link dev_guide.di About Dependency Injection}
|
||||
|
||||
|
||||
* {@link dev_guide.di.understanding_di Understanding DI in Angular}
|
||||
* {@link dev_guide.di.using_di_controllers Using DI in Controllers}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,6 @@
|
|||
@description
|
||||
|
||||
|
||||
|
||||
|
||||
* <a href="#H1_1">License</a>
|
||||
* <a href="#H1_2">Contributing to Source Code</a>
|
||||
* <a href="#H1_3">Applying Code Standards</a>
|
||||
|
|
@ -12,349 +10,243 @@
|
|||
* <a href="#H1_5">Submitting Your Changes</a>
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="H1_1"></a>
|
||||
# License
|
||||
|
||||
|
||||
`Angular` is an open source project licensed under the {@link
|
||||
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
|
||||
this page.
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="H1_2"></a>
|
||||
# Contributing to Source Code
|
||||
|
||||
|
||||
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:
|
||||
|
||||
|
||||
* 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
|
||||
coordinate our efforts, prevent duplication of work, and help you to craft the change so that it
|
||||
is successfully accepted upstream.
|
||||
|
||||
|
||||
* Small changes and bug fixes can be crafted and submitted to Github as a <a href="#H1_5">pull
|
||||
request</a>.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="H1_3"></a>
|
||||
# Applying Code Standards
|
||||
|
||||
|
||||
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 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
|
||||
check out the existing ngdocs.
|
||||
|
||||
|
||||
* 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
|
||||
Guide}:
|
||||
|
||||
|
||||
* Do not use namespaces: Instead, we wrap the entire `angular` code base in an anonymous closure
|
||||
and export our API explicitly rather than implicitly.
|
||||
|
||||
|
||||
* Wrap all code at 100 characters.
|
||||
|
||||
|
||||
* Instead of complex inheritance hierarchies, we prefer simple objects. We use prototypical
|
||||
inheritance only when absolutely necessary.
|
||||
|
||||
|
||||
* 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
|
||||
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
|
||||
that is used throughout `angular`. The best guidance is to do what makes the most sense.
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="H1_4"></a>
|
||||
# Checking Out and Building Angular
|
||||
|
||||
|
||||
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`:
|
||||
|
||||
|
||||
|
||||
|
||||
## Installation Dependencies
|
||||
|
||||
|
||||
Before you can build `angular`, you must install or configure the following dependencies on your
|
||||
machine:
|
||||
|
||||
|
||||
* {@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
|
||||
Rake website.
|
||||
|
||||
|
||||
* {@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
|
||||
pre-packaged bundle.
|
||||
|
||||
|
||||
* 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
|
||||
`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
|
||||
quite a good source for information on Git.
|
||||
|
||||
|
||||
|
||||
|
||||
## Creating a Github Account and Forking Angular
|
||||
|
||||
|
||||
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
|
||||
https://github.com/angular/angular.js main angular repository}.
|
||||
|
||||
|
||||
|
||||
|
||||
## Building `Angular`
|
||||
|
||||
|
||||
To build `angular`, you check out the source code and use Rake to generate the non-minified and
|
||||
minified `angular` files:
|
||||
|
||||
|
||||
1. To clone your Github repository, run:
|
||||
|
||||
|
||||
git clone git@github.com:<github username>/angular.js.git
|
||||
|
||||
|
||||
2. To go to the `angular` directory, run:
|
||||
|
||||
|
||||
cd angular.js
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
4. To build `angular`, run:
|
||||
|
||||
|
||||
rake package
|
||||
|
||||
|
||||
The build output can be located under the `build` directory. It consists of the following files and
|
||||
directories:
|
||||
|
||||
|
||||
* `angular-<version>.tgz` — This is the complete tarball, which contains all of the release build
|
||||
artifacts.
|
||||
|
||||
|
||||
* `angular.js` — The non-minified `angular` script.
|
||||
|
||||
|
||||
* `angular.min.js` — The minified `angular` script.
|
||||
|
||||
|
||||
* `angular-scenario.js` — The `angular` End2End test runner.
|
||||
|
||||
|
||||
* `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/index.html` — The main page for the documentation.
|
||||
|
||||
|
||||
* `docs/docs-scenario.html` — The End2End test runner for the documentation application.
|
||||
|
||||
|
||||
|
||||
|
||||
## 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
|
||||
made available a local web server based on Node.js.
|
||||
|
||||
|
||||
1. To start the web server, run:
|
||||
|
||||
|
||||
./nodeserver.sh
|
||||
|
||||
|
||||
2. To access the local server, go to this website:
|
||||
|
||||
|
||||
http://localhost:8000/
|
||||
|
||||
|
||||
By default, it serves the contents of the `angular` project directory.
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="unit-tests"></a>
|
||||
## Running the Unit Test Suite
|
||||
|
||||
|
||||
Our unit and integration tests are written with Jasmine and executed with JsTestDriver. To run the
|
||||
tests:
|
||||
|
||||
|
||||
1. To start the JSTD server, run:
|
||||
|
||||
|
||||
./server.sh
|
||||
|
||||
|
||||
2. To capture one or more browsers, go to this website:
|
||||
|
||||
|
||||
http://localhost:9876/
|
||||
|
||||
|
||||
3. To trigger a test execution, run:
|
||||
|
||||
|
||||
./test.sh
|
||||
|
||||
|
||||
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:
|
||||
|
||||
|
||||
watchr watchr.rb
|
||||
|
||||
|
||||
5. To view the output of each test run, you can tail this log file:
|
||||
|
||||
|
||||
./logs/jstd.log
|
||||
|
||||
|
||||
|
||||
|
||||
## Running the End2End Test Suite
|
||||
|
||||
|
||||
To run the End2End test suite:
|
||||
|
||||
|
||||
1. Start the local web server.
|
||||
2. In a browser, go to:
|
||||
|
||||
|
||||
http://localhost:8000/build/docs/docs-scenario.html
|
||||
|
||||
|
||||
The tests are executed automatically.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="H1_5"></a>
|
||||
# Submitting Your Changes
|
||||
|
||||
|
||||
To create and submit a change:
|
||||
|
||||
|
||||
1. Create a new branch off the master for your changes:
|
||||
|
||||
|
||||
git branch my-fix-branch
|
||||
|
||||
|
||||
2. Check out the branch:
|
||||
|
||||
|
||||
git checkout my-fix-branch
|
||||
|
||||
|
||||
3. Create your patch, make sure to have plenty of tests (that pass).
|
||||
|
||||
|
||||
4. Commit your changes:
|
||||
|
||||
|
||||
git commit -a
|
||||
|
||||
|
||||
5. Run JavaScript Lint and be sure to address all new warnings and errors:
|
||||
|
||||
|
||||
rake lint
|
||||
|
||||
|
||||
6. Push your branch to Github:
|
||||
|
||||
|
||||
git push origin my-fix-branch
|
||||
|
||||
|
||||
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
|
||||
from the main (upstream) repository:
|
||||
|
||||
1. To delete the branch in Github, run:
|
||||
|
||||
|
||||
git push origin :my-fix-branch
|
||||
|
||||
|
||||
2. To check out the master branch, run:
|
||||
|
||||
|
||||
git checkout master
|
||||
|
||||
|
||||
3. To delete a local branch, run:
|
||||
|
||||
|
||||
git branch -D my-fix-branch
|
||||
|
||||
|
||||
4. To update your master with the latest upstream version, run:
|
||||
|
||||
|
||||
git pull --ff upstream master
|
||||
|
||||
|
||||
That's it! Thank you for your contribution!
|
||||
|
|
|
|||
|
|
@ -3,27 +3,22 @@
|
|||
@name Downloading
|
||||
@description
|
||||
|
||||
|
||||
# Including angular scripts from the angular server
|
||||
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
|
||||
local copy.
|
||||
|
||||
|
||||
There are two types of angular script URLs you can point to, one for development and one for
|
||||
production:
|
||||
|
||||
|
||||
* __angular-<version>.js__ — This is the human-readable, non-minified version, suitable for web
|
||||
development.
|
||||
* __angular-<version>.min.js__ — This is the minified version, which we strongly suggest you use in
|
||||
production.
|
||||
|
||||
|
||||
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:
|
||||
|
||||
|
||||
<pre>
|
||||
<!doctype html>
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
|
|
@ -37,51 +32,40 @@ example points to (non-minified) version 0.9.12:
|
|||
</pre>
|
||||
|
||||
|
||||
|
||||
|
||||
# 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
|
||||
angular files on their own servers.
|
||||
|
||||
|
||||
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
|
||||
lifetime). Each directory contains all artifacts that we released for a particular version.
|
||||
Download the version you want and have fun.
|
||||
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
|
||||
* __`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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
testing angular apps even easier. Your unit/integration test harness should load this file after
|
||||
`angular-<version>.js` is loaded.
|
||||
|
||||
|
||||
* __`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.
|
||||
|
||||
|
||||
* __`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
|
||||
our docs, or even more importantly, view the docs offline.
|
||||
|
|
|
|||
|
|
@ -3,49 +3,36 @@
|
|||
@name FAQ
|
||||
@description
|
||||
|
||||
|
||||
#FAQ
|
||||
|
||||
|
||||
### Why is this project called "angular"? Why is the namespace called "ng"?
|
||||
|
||||
|
||||
Because HTML has angular brackets and "ng" sounds like "angular".
|
||||
|
||||
|
||||
### Is <angular/> an HTML5 tag?
|
||||
|
||||
|
||||
No, <angular/> is not an HTML5 tag. angular is an orthogonal project to HTML5; you can use the two
|
||||
together.
|
||||
|
||||
|
||||
### 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,
|
||||
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.
|
||||
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?
|
||||
|
||||
|
||||
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
|
||||
attacks. angular does round-trip escaping on all strings for you.
|
||||
|
||||
|
||||
### Can I download the source, build, and host the angular environment locally?
|
||||
|
||||
|
||||
Yes. See instructions in {@link downloading}.
|
||||
|
||||
|
||||
### Is angular a templating system?
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
templating systems.
|
||||
|
||||
|
||||
### What browsers does angular work with?
|
||||
|
||||
|
||||
Webkit-based browsers (Safari, Chrome, iPhone, Android, WebOS, BlackBerry 6), Firefox, IE6 and
|
||||
above. Note that CSS only works on IE7 and above.
|
||||
|
||||
|
||||
### What's angular's performance like?
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
### How big is the angular bootstrap JS file that I need to include?
|
||||
|
||||
|
||||
The size of the library itself is < 50KB compressed and obfuscated.
|
||||
|
||||
|
||||
### 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}
|
||||
in angular.
|
||||
|
||||
|
||||
### Does angular use the jQuery 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
|
||||
{@link api/angular.element jQuery lite}. If jQuery is present in the path, angular uses it to
|
||||
manipulate the DOM.
|
||||
|
||||
|
||||
### What is testability like in angular?
|
||||
|
||||
|
||||
Very testable. It has an integrated dependency injection framework. See
|
||||
{@link api/angular.service service} for details.
|
||||
|
||||
|
||||
### How can I learn more about angular?
|
||||
|
||||
|
||||
Watch the July 28, 2010 talk
|
||||
"{@link http://www.youtube.com/watch?v=elvcgVSynRg| Angular: A Radically Different Way of Building
|
||||
AJAX Apps}".
|
||||
|
||||
|
||||
### How is angular licensed?
|
||||
|
||||
|
||||
The MIT License.
|
||||
|
|
|
|||
|
|
@ -2,89 +2,69 @@
|
|||
@name Getting Started
|
||||
@description
|
||||
|
||||
|
||||
# Hello World!
|
||||
|
||||
|
||||
A great way for you to get started with `angular` is to create the tradtional
|
||||
"Hello World!" app:
|
||||
|
||||
|
||||
1. In your favorite text editor, create an HTML file
|
||||
(for example, `helloworld.html`).
|
||||
2. From the __Source__ box below, copy and paste the code into your HTML file.
|
||||
(Double-click on the source to easily select all.)
|
||||
3. Open the file in your web browser.
|
||||
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
Hello {{'World'}}!
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
|
||||
|
||||
The resulting web page should look something like the following:
|
||||
|
||||
|
||||
<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
|
||||
the scenes.
|
||||
|
||||
|
||||
The first line of interest defines the `ng` namespace, which makes
|
||||
`angular` work across all browsers (especially important for IE):
|
||||
|
||||
|
||||
<pre>
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
</pre>
|
||||
|
||||
|
||||
The next line downloads the `angular` script, and instructs `angular` to process
|
||||
the entire HTML page when it is loaded:
|
||||
|
||||
|
||||
<pre>
|
||||
<script type="text/javascript" src="http://code.angularjs.org/angular-?.?.?.min.js"
|
||||
ng:autobind></script>
|
||||
</pre>
|
||||
|
||||
|
||||
(For details on what happens when `angular` processes an HTML page,
|
||||
see {@link guide/dev_guide.bootstrap Bootstrap}.)
|
||||
|
||||
|
||||
Finally, this line in the `<body>` of the page is the template that describes
|
||||
how to display our greeting in the UI:
|
||||
|
||||
|
||||
<pre>
|
||||
Hello {{'World'}}!
|
||||
</pre>
|
||||
|
||||
|
||||
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'.
|
||||
|
||||
|
||||
Next let's look at a more interesting example, that uses `angular` to
|
||||
bind a dynamic expression to our greeting text.
|
||||
|
||||
|
||||
# Hello <angular/> World!
|
||||
|
||||
|
||||
This example demonstrates `angular`'s two-way data binding:
|
||||
|
||||
|
||||
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.
|
||||
3. Refresh your browswer window.
|
||||
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
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:example>
|
||||
|
||||
|
||||
After the refresh, the page should look something like this:
|
||||
|
||||
|
||||
<img class="left" src="img/helloworld_2way.png" border="1" />
|
||||
|
||||
|
||||
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
|
||||
called `yourname`.
|
||||
* 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!
|
||||
|
||||
|
||||
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
|
||||
{@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).
|
||||
|
||||
|
||||
|
||||
|
||||
# Anatomy Of An Angular App
|
||||
|
||||
|
||||
This section describes the 3 parts of an angular app, and explains how they map to the
|
||||
Model-View-Controller design pattern:
|
||||
|
||||
|
||||
## Templates
|
||||
|
||||
|
||||
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
|
||||
extensible, meaning that with angular you can build your own declarative language on top of HTML!
|
||||
|
||||
|
||||
|
||||
|
||||
## Application Logic and Behavior
|
||||
|
||||
|
||||
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
|
||||
DOM manipulators, because they are built-in. This feature makes your application logic very easy to
|
||||
write, test, maintain, and understand.
|
||||
|
||||
|
||||
|
||||
|
||||
## Data
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
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:
|
||||
|
||||
|
||||
<img class="left" src="img/angular_parts.png" border="0" />
|
||||
|
||||
|
||||
In addition, angular comes with a set of Services, which have the following properties:
|
||||
|
||||
|
||||
* The services provided are very useful for building web applications.
|
||||
* You can extend and add application-specific behavior to services.
|
||||
* Services include Dependency-Injection, XHR, caching, URL routing, and browser abstraction.
|
||||
|
||||
|
||||
|
||||
|
||||
# Where To Go Next
|
||||
|
||||
|
||||
* For explanations and examples of the angular concepts presented on this page, see the {@link
|
||||
guide/index Developer Guide}.
|
||||
|
||||
|
||||
* 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}.
|
||||
|
|
|
|||
|
|
@ -2,20 +2,16 @@
|
|||
@name Tutorial
|
||||
@description
|
||||
|
||||
|
||||
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
|
||||
Android devices, lets you filter the list to see only devices that interest you, and then view
|
||||
details for any device.
|
||||
|
||||
|
||||
<img src="img/tutorial/catalog_screen.png">
|
||||
|
||||
|
||||
As you work through this tutorial, you will learn how angular makes browsers smarter — without the
|
||||
use of extensions or plugins.
|
||||
|
||||
|
||||
* 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.
|
||||
* 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
|
||||
your app, easier.
|
||||
|
||||
|
||||
And all of this works in any browser without modifications!
|
||||
|
||||
|
||||
When you finish the tutorial you will be able to:
|
||||
|
||||
|
||||
* Create a dynamic application that works in any browser
|
||||
* Define the differences between angular and common JavaScript frameworks
|
||||
* 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
|
||||
* Identify resources for learning more about angular
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
really digging into it. If you're looking for a shorter introduction to angular, check out the
|
||||
{@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
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
one you prefer:
|
||||
|
||||
|
||||
<doc:tutorial-instructions show="true">
|
||||
<doc:tutorial-instruction id="git-mac" title="Git on Mac/Linux">
|
||||
<ol>
|
||||
|
|
@ -95,7 +77,6 @@ server.</p></li>
|
|||
</ol>
|
||||
</doc:tutorial-instruction>
|
||||
|
||||
|
||||
<doc:tutorial-instruction id="git-win" title="Git on Windows">
|
||||
<ol>
|
||||
<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>
|
||||
</doc:tutorial-instruction>
|
||||
|
||||
|
||||
<doc:tutorial-instruction id="ss-mac" title="Snapshots on Mac/Linux">
|
||||
<ol>
|
||||
<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>
|
||||
</doc:tutorial-instruction>
|
||||
|
||||
|
||||
<doc:tutorial-instruction id="ss-win" title="Snapshots on Windows">
|
||||
<ol>
|
||||
<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-instructions>
|
||||
|
||||
|
||||
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}.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,19 +2,14 @@
|
|||
@name Tutorial: 0 - angular-seed
|
||||
@description
|
||||
|
||||
|
||||
<ul doc:tutorial-nav="0"></ul>
|
||||
|
||||
|
||||
|
||||
|
||||
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
|
||||
angular-seed, and run the application in the browser.
|
||||
|
||||
|
||||
|
||||
|
||||
<doc:tutorial-instructions show="true">
|
||||
<doc:tutorial-instruction id="git-mac" title="Git on Mac/Linux">
|
||||
<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
|
||||
your working directory to be lost.</p></li>
|
||||
|
||||
|
||||
<li>To see the app running in a browser, do one of the following:
|
||||
<ul>
|
||||
<li><b>For node.js users:</b>
|
||||
|
|
@ -50,8 +44,6 @@ directory.</li>
|
|||
</doc:tutorial-instruction>
|
||||
|
||||
|
||||
|
||||
|
||||
<doc:tutorial-instruction id="git-win" title="Git on Windows">
|
||||
<ol>
|
||||
<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 id="ss-mac" title="Snapshots on Mac/Linux">
|
||||
<ol>
|
||||
<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 id="ss-win" title="Snapshots on Windows">
|
||||
<ol>
|
||||
<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>
|
||||
|
||||
|
||||
|
||||
|
||||
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
|
||||
below. The code contains some key angular elements that we will need going forward.
|
||||
|
||||
|
||||
__`app/index.html`:__
|
||||
<pre>
|
||||
<!doctype html>
|
||||
|
|
@ -173,10 +157,8 @@ __`app/index.html`:__
|
|||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
Nothing here yet!
|
||||
|
||||
|
||||
<script src="lib/angular/angular.js" ng:autobind></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -184,73 +166,51 @@ __`app/index.html`:__
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## What is the code doing?
|
||||
|
||||
|
||||
* xmlns declaration
|
||||
|
||||
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
|
||||
|
||||
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
|
||||
using XHTML or HTML).
|
||||
|
||||
|
||||
* angular script tag
|
||||
|
||||
|
||||
<script src="lib/angular/angular.js" ng:autobind>
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
`ng:autobind`, it creates a root scope for the application and associates it with the `<html>`
|
||||
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
|
||||
about this in the next steps.
|
||||
|
||||
|
||||
|
||||
|
||||
## What are all these files in my working directory?
|
||||
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
|
||||
For the purposes of this tutorial, we modified the angular-seed with the following changes:
|
||||
|
||||
|
||||
* Removed the example app
|
||||
* Added phone images to `app/img/phones`
|
||||
* Added phone data files (JSON) to `app/phones`
|
||||
|
||||
|
||||
|
||||
|
||||
# Summary
|
||||
|
||||
|
||||
Now let's go to step 1 and add some content to the web app.
|
||||
|
||||
|
||||
|
||||
|
||||
<ul doc:tutorial-nav="0"></ul>
|
||||
|
|
|
|||
|
|
@ -2,34 +2,24 @@
|
|||
@name Tutorial: 1 - Static Template
|
||||
@description
|
||||
|
||||
|
||||
<ul doc:tutorial-nav="1"></ul>
|
||||
|
||||
|
||||
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
|
||||
|
||||
|
||||
<doc:tutorial-instructions step="1" show="true"></doc:tutorial-instructions>
|
||||
|
||||
|
||||
|
||||
|
||||
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
|
||||
https://github.com/angular/angular-phonecat/compare/step-0...step-1 GitHub}:
|
||||
|
||||
|
||||
__`app/index.html`:__
|
||||
<pre>
|
||||
...
|
||||
|
|
@ -51,27 +41,17 @@ __`app/index.html`:__
|
|||
</pre>
|
||||
|
||||
|
||||
|
||||
|
||||
# Experiments
|
||||
|
||||
|
||||
* Try adding more static HTML to `index.html`. For example:
|
||||
|
||||
|
||||
<p>Total number of phones: 2</p>
|
||||
|
||||
|
||||
|
||||
|
||||
# Summary
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
|
||||
|
||||
<ul doc:tutorial-nav="1"></ul>
|
||||
|
||||
|
|
|
|||
|
|
@ -2,50 +2,36 @@
|
|||
@name Tutorial: 2 - Angular Template
|
||||
@description
|
||||
|
||||
|
||||
<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
|
||||
code for the controller we are going to add.
|
||||
|
||||
|
||||
There are many ways to structure the code for an application. With angular, we encourage the use of
|
||||
{@link http://en.wikipedia.org/wiki/Model–View–Controller the MVC design pattern} to decouple the
|
||||
code and separate concerns. With that in mind, let's use a little angular and JavaScript to add
|
||||
model, view, and controller components to our app.
|
||||
|
||||
|
||||
|
||||
|
||||
<doc:tutorial-instructions step="2"></doc:tutorial-instructions>
|
||||
|
||||
|
||||
|
||||
|
||||
The app now contains a list with 3 phones.
|
||||
|
||||
|
||||
The most important changes are listed below. You can see the full diff on {@link
|
||||
https://github.com/angular/angular-phonecat/compare/step-1...step-2 GitHub}:
|
||||
|
||||
|
||||
|
||||
|
||||
## Template for the View
|
||||
|
||||
|
||||
The __view__ component is constructed by angular from this template:
|
||||
|
||||
|
||||
__`app/index.html`:__
|
||||
<pre>
|
||||
...
|
||||
<body ng:controller="PhoneListCtrl">
|
||||
|
||||
|
||||
<ul>
|
||||
<li ng:repeat="phone in phones">
|
||||
{{phone.name}}
|
||||
|
|
@ -53,27 +39,22 @@ __`app/index.html`:__
|
|||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<script src="lib/angular/angular.js" ng:autobind></script>
|
||||
<script src="js/controllers.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
</pre>
|
||||
|
||||
|
||||
We replaced the hard-coded phone list with the {@link api/angular.widget.@ng:repeat ng:repeat
|
||||
widget} and two {@link guide/dev_guide.expressions angular expressions} enclosed in curly braces:
|
||||
`{{phone.name}}` and `{{phone.snippet}}`:
|
||||
|
||||
|
||||
* The `ng:repeat="phone in phones"` statement in the `<li>` tag is an angular repeater. It tells
|
||||
angular to create a `<li>` element for each phone in the phones list, using the first `<li>` tag as
|
||||
the template.
|
||||
|
||||
|
||||
<img src="img/tutorial/tutorial_02_final.png">
|
||||
|
||||
|
||||
* The curly braces around `phone.name` and `phone.snippet` are an example of {@link
|
||||
guide/dev_guide.compiler.markup angular markup}. The curly markup is shorthand for the angular
|
||||
directive {@link api/angular.directive.ng:bind ng:bind}. 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.
|
||||
|
||||
|
||||
|
||||
|
||||
## Model and Controller
|
||||
|
||||
|
||||
The data __model__ (a simple array of phones in object literal notation) is instantiated within
|
||||
the __controller__ function (`PhoneListCtrl`):
|
||||
|
||||
|
||||
__`app/js/controllers.js`:__
|
||||
<pre>
|
||||
function PhoneListCtrl() {
|
||||
|
|
@ -107,22 +84,16 @@ function PhoneListCtrl() {
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Although the controller is not yet doing very much controlling, it is playing a crucial role. By
|
||||
providing context for our data model, the controller allows us to establish data-binding between
|
||||
the model and the view. Note in the following how we connected the dots between our presentation,
|
||||
data, and logic components:
|
||||
|
||||
|
||||
* The name of our controller function (in the JavaScript file `controllers.js`) matches the {@link
|
||||
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
|
||||
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
|
||||
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
|
||||
|
|
@ -131,23 +102,17 @@ reflected in the model. To learn more about angular scopes, see the {@link api/a
|
|||
angular scope documentation}.
|
||||
|
||||
|
||||
|
||||
|
||||
## Tests
|
||||
|
||||
|
||||
The "Angular way" makes it easy for us to test as we develop; the unit test for your newly created
|
||||
controller looks as follows:
|
||||
|
||||
|
||||
__`test/unit/controllersSpec.js`:__
|
||||
<pre>
|
||||
describe('PhoneCat controllers', function() {
|
||||
|
||||
|
||||
describe('PhoneListCtrl', function() {
|
||||
|
||||
|
||||
it('should create "phones" model with 3 phones', function() {
|
||||
var ctrl = new PhoneListCtrl();
|
||||
expect(ctrl.phones.length).toBe(3);
|
||||
|
|
@ -156,105 +121,76 @@ describe('PhoneCat controllers', function() {
|
|||
});
|
||||
</pre>
|
||||
|
||||
|
||||
Ease of testing is another cornerstone of angular's design philosophy. All we are doing here is
|
||||
showing how easy it is to create a unit test. The test verifies that we have 3 records in the
|
||||
phones array.
|
||||
|
||||
|
||||
Angular developers prefer the syntax of Jasmine's Behavior-driven Development (BDD) framework when
|
||||
writing tests. Although Jasmine is not required by angular, we used it to write all tests in this
|
||||
tutorial. You can learn about Jasmine on the {@link http://pivotal.github.com/jasmine/ Jasmine home
|
||||
page} and on the {@link https://github.com/pivotal/jasmine/wiki Jasmine wiki}.
|
||||
|
||||
|
||||
The angular-seed project is pre-configured to run all unit tests using {@link
|
||||
http://code.google.com/p/js-test-driver/ JsTestDriver}. To run the test, do the following:
|
||||
|
||||
|
||||
1. In a _separate_ terminal window or tab, go to the `angular-phonecat` directory and run
|
||||
`./scripts/test-server.sh` to start the test web server.
|
||||
|
||||
|
||||
2. Open a new browser tab or window and navigate to {@link http://localhost:9876}.
|
||||
|
||||
|
||||
3. Choose "Capture this browser in strict mode".
|
||||
|
||||
|
||||
At this point, you can leave this tab open and forget about it. JsTestDriver will use it to
|
||||
execute the tests and report the results in the terminal.
|
||||
|
||||
|
||||
4. Execute the test by running `./scripts/test.sh`
|
||||
|
||||
|
||||
You should see the following or similar output:
|
||||
|
||||
|
||||
Chrome: Runner reset.
|
||||
.
|
||||
Total 1 tests (Passed: 1; Fails: 0; Errors: 0) (2.00 ms)
|
||||
Chrome 11.0.696.57 Mac OS: Run 1 tests (Passed: 1; Fails: 0; Errors 0) (2.00 ms)
|
||||
|
||||
|
||||
Yay! The test passed! Or not...
|
||||
|
||||
|
||||
Note: If you see errors after you run the test, close the browser tab and go back to the terminal
|
||||
and kill the script, then repeat the procedure above.
|
||||
|
||||
|
||||
# Experiments
|
||||
|
||||
|
||||
* Add another binding to `index.html`. For example:
|
||||
|
||||
|
||||
<p>Total number of phones: {{phones.length}}</p>
|
||||
|
||||
|
||||
* Create a new model property in the controller and bind to it from the template. For example:
|
||||
|
||||
|
||||
this.hello = "Hello, World!"
|
||||
|
||||
|
||||
Refresh your browser to make sure it says, "Hello, World!"
|
||||
|
||||
|
||||
* Create a repeater that constructs a simple table:
|
||||
|
||||
|
||||
<table>
|
||||
<tr><th>row number</th></tr>
|
||||
<tr ng:repeat="i in [0, 1, 2, 3, 4, 5, 6, 7]"><td>{{i}}</td></tr>
|
||||
</table>
|
||||
|
||||
|
||||
Now, make the list 1-based by incrementing `i` by one in the binding:
|
||||
|
||||
|
||||
<table>
|
||||
<tr><th>row number</th></tr>
|
||||
<tr ng:repeat="i in [0, 1, 2, 3, 4, 5, 6, 7]"><td>{{i+1}}</td></tr>
|
||||
</table>
|
||||
|
||||
|
||||
* Make the unit test fail by changing the `toBe(3)` statement to `toBe(4)`, and rerun the
|
||||
`./scripts/test.sh` script.
|
||||
|
||||
|
||||
|
||||
|
||||
# Summary
|
||||
|
||||
|
||||
You now have a dynamic app that features separate model, view, and controller components, and
|
||||
you'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>
|
||||
|
||||
|
|
|
|||
|
|
@ -2,53 +2,38 @@
|
|||
@name Tutorial: 3 - Filtering Repeaters
|
||||
@description
|
||||
|
||||
|
||||
<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
|
||||
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
|
||||
quickly detects regressions.
|
||||
|
||||
|
||||
|
||||
|
||||
<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
|
||||
into the search box.
|
||||
|
||||
|
||||
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
|
||||
GitHub}:
|
||||
|
||||
|
||||
|
||||
|
||||
## Controller
|
||||
|
||||
|
||||
We made no changes to the controller.
|
||||
|
||||
|
||||
|
||||
|
||||
## Template
|
||||
|
||||
|
||||
__`app/index.html`:__
|
||||
<pre>
|
||||
...
|
||||
Fulltext Search: <input name="query"/>
|
||||
|
||||
|
||||
<ul class="phones">
|
||||
<li ng:repeat="phone in phones.$filter(query)">
|
||||
{{phone.name}}
|
||||
|
|
@ -58,70 +43,54 @@ __`app/index.html`:__
|
|||
...
|
||||
</pre>
|
||||
|
||||
|
||||
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`.
|
||||
|
||||
|
||||
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:
|
||||
|
||||
|
||||
* 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.
|
||||
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
|
||||
<img src="img/tutorial/tutorial_03_final.png">
|
||||
|
||||
|
||||
* 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`.
|
||||
|
||||
|
||||
`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.
|
||||
|
||||
|
||||
## Test
|
||||
|
||||
|
||||
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
|
||||
test DOM manipulation or the wiring of our application. For these, an end-to-end test is a much
|
||||
better choice.
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
__`test/e2e/scenarios.js`:__
|
||||
<pre>
|
||||
describe('PhoneCat App', function() {
|
||||
|
||||
|
||||
describe('Phone list view', function() {
|
||||
|
||||
|
||||
beforeEach(function() {
|
||||
browser().navigateTo('../../app/index.html');
|
||||
});
|
||||
|
||||
|
||||
it('should filter the phone list as user types into the search box', function() {
|
||||
expect(repeater('.phones li').count()).toBe(3);
|
||||
|
||||
|
||||
input('query').enter('nexus');
|
||||
expect(repeater('.phones li').count()).toBe(1);
|
||||
|
||||
|
||||
input('query').enter('motorola');
|
||||
expect(repeater('.phones li').count()).toBe(2);
|
||||
});
|
||||
|
|
@ -129,64 +98,48 @@ describe('PhoneCat App', function() {
|
|||
});
|
||||
</pre>
|
||||
|
||||
|
||||
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
|
||||
https://docs.google.com/document/d/11L8htLKrh6c92foV71ytYpiKkeKpM4_a5-9c3HywfIc/edit?hl=en&pli=1#
|
||||
angular's end-to-end test runner}.
|
||||
|
||||
|
||||
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}
|
||||
* users with other http servers:
|
||||
`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}
|
||||
|
||||
|
||||
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
|
||||
really is that easy to set up any functional, readable, end-to-end test.
|
||||
|
||||
|
||||
# Experiments
|
||||
|
||||
|
||||
* 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.
|
||||
|
||||
|
||||
* 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:
|
||||
|
||||
|
||||
<title>Google Phone Gallery: {{query}}</title>
|
||||
|
||||
|
||||
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:
|
||||
|
||||
|
||||
<body ng:controller="PhoneListCtrl">
|
||||
|
||||
|
||||
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
|
||||
and title elements:
|
||||
|
||||
|
||||
<html ng:controller="PhoneListCtrl">
|
||||
|
||||
|
||||
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`:
|
||||
|
||||
|
||||
<pre>
|
||||
it('should display the current filter value within an element with id "status"',
|
||||
function() {
|
||||
|
|
@ -201,30 +154,21 @@ and title elements:
|
|||
});
|
||||
</pre>
|
||||
|
||||
|
||||
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
|
||||
make the test pass.
|
||||
|
||||
|
||||
* 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
|
||||
app is live! Change the search query to prove it. This is great for troubleshooting end-to-end
|
||||
tests.
|
||||
|
||||
|
||||
|
||||
|
||||
# Summary
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
|
||||
|
||||
<ul doc:tutorial-nav="3"></ul>
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -2,38 +2,27 @@
|
|||
@name Tutorial: 4 - Two-way Data Binding
|
||||
@description
|
||||
|
||||
|
||||
<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
|
||||
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.
|
||||
|
||||
|
||||
|
||||
|
||||
<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
|
||||
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
|
||||
https://github.com/angular/angular-phonecat/compare/step-3...step-4
|
||||
GitHub}:
|
||||
|
||||
|
||||
|
||||
|
||||
## Template
|
||||
|
||||
|
||||
__`app/index.html`:__
|
||||
<pre>
|
||||
...
|
||||
|
|
@ -50,7 +39,6 @@ __`app/index.html`:__
|
|||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<ul class="phones">
|
||||
<li ng:repeat="phone in phones.$filter(query).$orderBy(orderProp)">
|
||||
{{phone.name}}
|
||||
|
|
@ -60,26 +48,20 @@ __`app/index.html`:__
|
|||
...
|
||||
</pre>
|
||||
|
||||
|
||||
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
|
||||
two provided sorting options.
|
||||
|
||||
|
||||
<img src="img/tutorial/tutorial_04-06_final.png">
|
||||
|
||||
|
||||
* 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
|
||||
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.
|
||||
`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
|
||||
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
|
||||
|
|
@ -87,17 +69,12 @@ necessary!
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Controller
|
||||
|
||||
|
||||
__`app/js/controller.js`:__
|
||||
<pre>
|
||||
/* App Controllers */
|
||||
|
||||
|
||||
function PhoneListCtrl() {
|
||||
this.phones = [{"name": "Nexus S",
|
||||
"snippet": "Fast just got faster with Nexus S.",
|
||||
|
|
@ -109,21 +86,17 @@ function PhoneListCtrl() {
|
|||
"snippet": "The Next, Next Generation tablet.",
|
||||
"age": 2}];
|
||||
|
||||
|
||||
this.orderProp = 'age';
|
||||
}
|
||||
</pre>
|
||||
|
||||
|
||||
* 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.
|
||||
|
||||
|
||||
* 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
|
||||
(`'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
|
||||
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
|
||||
|
|
@ -133,39 +106,28 @@ to the model.
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Test
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
__`test/unit/controllerSpec.js`:__
|
||||
<pre>
|
||||
describe('PhoneCat controllers', function() {
|
||||
|
||||
|
||||
describe('PhoneListCtrl', function(){
|
||||
var scope, $browser, ctrl;
|
||||
|
||||
|
||||
beforeEach(function() {
|
||||
ctrl = new PhoneListCtrl();
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
it('should create "phones" model with 3 phones', function() {
|
||||
expect(ctrl.phones.length).toBe(3);
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
it('should set the default value of orderProp model', function() {
|
||||
expect(ctrl.orderProp).toBe('age');
|
||||
});
|
||||
|
|
@ -174,49 +136,37 @@ describe('PhoneCat controllers', function() {
|
|||
</pre>
|
||||
|
||||
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
following output.
|
||||
|
||||
|
||||
Chrome: Runner reset.
|
||||
..
|
||||
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)
|
||||
|
||||
|
||||
|
||||
|
||||
Let's turn our attention to the end-to-end test.
|
||||
|
||||
|
||||
__`test/e2e/scenarios.js`:__
|
||||
<pre>
|
||||
...
|
||||
it('should be possible to control phone order via the drop down select box',
|
||||
function() {
|
||||
|
||||
|
||||
// narrow the dataset to make the test assertions shorter
|
||||
input('query').enter('tablet');
|
||||
|
||||
|
||||
expect(repeater('.phones li', 'Phone List').column('a')).
|
||||
toEqual(["Motorola XOOM\u2122 with Wi-Fi",
|
||||
"MOTOROLA XOOM\u2122"]);
|
||||
|
||||
|
||||
select('orderProp').option('alphabetical');
|
||||
|
||||
|
||||
expect(repeater('.phones li', 'Phone List').column('a')).
|
||||
toEqual(["MOTOROLA XOOM\u2122",
|
||||
"Motorola XOOM\u2122 with Wi-Fi"]);
|
||||
|
|
@ -224,36 +174,26 @@ __`test/e2e/scenarios.js`:__
|
|||
...
|
||||
</pre>
|
||||
|
||||
|
||||
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
|
||||
can see them running on {@link
|
||||
http://angular.github.com/angular-phonecat/step-4/test/e2e/runner.html
|
||||
angular's server}.
|
||||
|
||||
|
||||
# Experiments
|
||||
|
||||
|
||||
* 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
|
||||
"Alphabetical".
|
||||
|
||||
|
||||
* Add an `{{orderProp}}` binding into the `index.html` template to display its current value as
|
||||
text.
|
||||
|
||||
|
||||
# Summary
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
|
||||
|
||||
<ul doc:tutorial-nav="4"></ul>
|
||||
|
||||
|
|
|
|||
|
|
@ -2,40 +2,29 @@
|
|||
@name Tutorial: 5 - XHRs & Dependency Injection
|
||||
@description
|
||||
|
||||
|
||||
<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
|
||||
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
|
||||
injection (DI)} to provide the service to the `PhoneListCtrl` controller.
|
||||
|
||||
|
||||
|
||||
|
||||
<doc:tutorial-instructions step="5"></doc:tutorial-instructions>
|
||||
|
||||
|
||||
|
||||
|
||||
You should now see a list of 20 phones.
|
||||
|
||||
|
||||
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
|
||||
GitHub}:
|
||||
|
||||
|
||||
## Data
|
||||
|
||||
|
||||
The `app/phones/phone.json` file in your project is a dataset that contains a larger list of phones
|
||||
stored in the JSON format.
|
||||
|
||||
|
||||
Following is a sample of the file:
|
||||
<pre>
|
||||
[
|
||||
|
|
@ -51,125 +40,96 @@ Following is a sample of the file:
|
|||
</pre>
|
||||
|
||||
|
||||
|
||||
|
||||
## Controller
|
||||
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
|
||||
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,
|
||||
and control) and loosely coupled (dependencies between components are not resolved by the
|
||||
components themselves, but by the DI subsystem).
|
||||
|
||||
|
||||
__`app/js/controllers.js:`__
|
||||
<pre>
|
||||
function PhoneListCtrl($xhr) {
|
||||
var self = this;
|
||||
|
||||
|
||||
$xhr('GET', 'phones/phones.json', function(code, response) {
|
||||
self.phones = response;
|
||||
});
|
||||
|
||||
|
||||
self.orderProp = 'age';
|
||||
}
|
||||
|
||||
|
||||
//PhoneListCtrl.$inject = ['$xhr'];
|
||||
</pre>
|
||||
|
||||
|
||||
`$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.
|
||||
(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
|
||||
tutorial.)
|
||||
|
||||
|
||||
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
|
||||
`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
|
||||
the controller's constructor function, as follows:
|
||||
|
||||
|
||||
function PhoneListCtrl($xhr) {...}
|
||||
|
||||
|
||||
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
|
||||
service may have (services often depend upon other services).
|
||||
|
||||
|
||||
<img src="img/tutorial/xhr_service_final.png">
|
||||
|
||||
|
||||
|
||||
|
||||
### '$' Prefix Naming Convention
|
||||
|
||||
|
||||
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 '$'
|
||||
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.
|
||||
|
||||
|
||||
### A Note on Minification
|
||||
|
||||
|
||||
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)
|
||||
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.
|
||||
|
||||
|
||||
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
|
||||
(commented out) suggests:
|
||||
|
||||
|
||||
PhoneListCtrl.$inject = ['$xhr'];
|
||||
|
||||
|
||||
|
||||
|
||||
## Test
|
||||
|
||||
|
||||
__`test/unit/controllersSpec.js`:__
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
production code behind the scenes, as follows:
|
||||
|
||||
|
||||
<pre>
|
||||
describe('PhoneCat controllers', function() {
|
||||
|
||||
|
||||
describe('PhoneListCtrl', function() {
|
||||
var scope, $browser, ctrl;
|
||||
|
||||
|
||||
beforeEach(function() {
|
||||
scope = angular.scope();
|
||||
$browser = scope.$service('$browser');
|
||||
|
||||
|
||||
$browser.xhr.expectGET('phones/phones.json')
|
||||
.respond([{name: 'Nexus S'},
|
||||
{name: 'Motorola DROID'}]);
|
||||
|
|
@ -178,59 +138,46 @@ describe('PhoneCat controllers', function() {
|
|||
});
|
||||
</pre>
|
||||
|
||||
|
||||
We created the controller in the test environment, as follows:
|
||||
|
||||
|
||||
* 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
|
||||
the `PhoneListCtrl` controller
|
||||
|
||||
|
||||
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
|
||||
incoming request from the controller. To do this we:
|
||||
|
||||
|
||||
* 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 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.
|
||||
|
||||
|
||||
* 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
|
||||
the `$browser.xhr.flush` method.
|
||||
|
||||
|
||||
Now, we will make assertions to verify that the `phones` model doesn't exist on the scope, before
|
||||
the response is received:
|
||||
|
||||
|
||||
<pre>
|
||||
it('should create "phones" model with 2 phones fetched from xhr', function() {
|
||||
expect(ctrl.phones).toBeUndefined();
|
||||
$browser.xhr.flush();
|
||||
|
||||
|
||||
expect(ctrl.phones).toEqual([{name: 'Nexus S'},
|
||||
{name: 'Motorola DROID'}]);
|
||||
});
|
||||
</pre>
|
||||
|
||||
|
||||
* 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 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:
|
||||
|
||||
|
||||
<pre>
|
||||
it('should set the default value of orderProp model', function() {
|
||||
expect(ctrl.orderProp).toBe('age');
|
||||
|
|
@ -239,44 +186,31 @@ Finally, we verify that the default value of `orderProp` is set correctly:
|
|||
});
|
||||
</pre>
|
||||
|
||||
|
||||
To run the unit tests, execute the `./scripts/test.sh` script and you should see the following
|
||||
output.
|
||||
|
||||
|
||||
Chrome: Runner reset.
|
||||
..
|
||||
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)
|
||||
|
||||
|
||||
|
||||
|
||||
# Experiments
|
||||
|
||||
|
||||
* At the bottom of `index.html`, add a `{{phones}}` binding to see the list of phones displayed in
|
||||
json format.
|
||||
|
||||
|
||||
* 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:
|
||||
|
||||
|
||||
self.phones = response.splice(0, 5);
|
||||
|
||||
|
||||
|
||||
|
||||
# Summary
|
||||
|
||||
|
||||
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
|
||||
phones and some links.
|
||||
|
||||
|
||||
|
||||
|
||||
<ul doc:tutorial-nav="5"></ul>
|
||||
|
||||
|
|
|
|||
|
|
@ -2,41 +2,29 @@
|
|||
@name Tutorial: 6 - Templating Links & Images
|
||||
@description
|
||||
|
||||
|
||||
<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
|
||||
now, will go nowhere. In subsequent steps you will use the links to display additional information
|
||||
about the phones in the catalog.
|
||||
|
||||
|
||||
|
||||
|
||||
<doc:tutorial-instructions step="6"></doc:tutorial-instructions>
|
||||
|
||||
|
||||
|
||||
|
||||
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
|
||||
https://github.com/angular/angular-phonecat/compare/step-5...step-6
|
||||
GitHub}:
|
||||
|
||||
|
||||
|
||||
|
||||
## Data
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
__`app/phones/phones.json`__ (sample snippet):
|
||||
<pre>
|
||||
[
|
||||
|
|
@ -52,11 +40,8 @@ __`app/phones/phones.json`__ (sample snippet):
|
|||
</pre>
|
||||
|
||||
|
||||
|
||||
|
||||
## Template
|
||||
|
||||
|
||||
__`app/index.html`:__
|
||||
<pre>
|
||||
...
|
||||
|
|
@ -70,13 +55,11 @@ __`app/index.html`:__
|
|||
...
|
||||
</pre>
|
||||
|
||||
|
||||
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`
|
||||
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.
|
||||
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
|
||||
|
||||
|
||||
## Test
|
||||
|
||||
|
||||
__`test/e2e/scenarios.js`__:
|
||||
<pre>
|
||||
...
|
||||
|
|
@ -100,36 +80,26 @@ __`test/e2e/scenarios.js`__:
|
|||
...
|
||||
</pre>
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
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
|
||||
http://angular.github.com/angular-phonecat/step-6/test/e2e/runner.html
|
||||
angular's server}.
|
||||
|
||||
|
||||
# Experiments
|
||||
|
||||
|
||||
* 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
|
||||
making an extraneous request to `/app/%7B%7Bphone.imageUrl%7D%7D` (or
|
||||
`/app/index.html/{{phone.imageUrl}}`).
|
||||
|
||||
|
||||
|
||||
|
||||
# Summary
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
|
||||
|
||||
<ul doc:tutorial-nav="6"></ul>
|
||||
|
||||
|
|
|
|||
|
|
@ -2,52 +2,38 @@
|
|||
@name Tutorial: 7 - Routing & Multiple Views
|
||||
@description
|
||||
|
||||
|
||||
<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
|
||||
multiple views by adding routing.
|
||||
|
||||
|
||||
|
||||
|
||||
<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
|
||||
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
|
||||
https://github.com/angular/angular-phonecat/compare/step-6...step-7
|
||||
GitHub}:
|
||||
|
||||
|
||||
|
||||
|
||||
## Multiple Views, Routing and Layout Template
|
||||
|
||||
|
||||
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
|
||||
`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.
|
||||
|
||||
|
||||
To add the detailed view, we could expand the `index.html` file to contain template code for both
|
||||
views, but that would get messy very quickly. Instead, we are going to turn the `index.html`
|
||||
template into what we call a "layout template". This is a template that is common for all views in
|
||||
our application. Other "partial templates" are then included into this layout template depending on
|
||||
the current "route" — the view that is currently displayed to the user.
|
||||
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
|
||||
|
||||
|
||||
## Controllers
|
||||
|
||||
|
||||
__`app/js/controller.js`:__
|
||||
<pre>
|
||||
function PhoneCatCtrl($route) {
|
||||
var self = this;
|
||||
|
||||
|
||||
$route.when('/phones',
|
||||
{template: 'partials/phone-list.html', controller: PhoneListCtrl});
|
||||
$route.when('/phones/:phoneId',
|
||||
{template: 'partials/phone-detail.html', controller: PhoneDetailCtrl});
|
||||
$route.otherwise({redirectTo: '/phones'});
|
||||
|
||||
|
||||
$route.onChange(function() {
|
||||
self.params = $route.current.params;
|
||||
});
|
||||
|
||||
|
||||
$route.parent(this);
|
||||
}
|
||||
|
||||
|
||||
//PhoneCatCtrl.$inject = ['$route'];
|
||||
...
|
||||
</pre>
|
||||
|
||||
|
||||
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:
|
||||
|
||||
|
||||
* 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.
|
||||
|
||||
|
||||
* 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
|
||||
`phone-detail.html` template and the `PhoneDetailCtrl` controller.
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
The statement `$route.otherwise({redirectTo: '/phones'})` triggers a redirection to `/phones` when
|
||||
the browser address doesn't match either of our routes.
|
||||
|
||||
|
||||
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
|
||||
"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
|
||||
controller.
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
allows us to use the `phoneId` property of this map in the `phone-details.html` template.
|
||||
|
||||
|
||||
|
||||
|
||||
## Template
|
||||
|
||||
|
||||
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
|
||||
route into the layout template, which makes it a perfect fit for our `index.html` template.
|
||||
|
||||
|
||||
__`app/index.html`:__
|
||||
<pre>
|
||||
...
|
||||
<body ng:controller="PhoneCatCtrl">
|
||||
|
||||
|
||||
<ng:view></ng:view>
|
||||
|
||||
|
||||
<script src="lib/angular/angular.js" ng:autobind></script>
|
||||
<script src="js/controllers.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
</pre>
|
||||
|
||||
|
||||
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`
|
||||
template:
|
||||
|
||||
|
||||
__`app/partials/phone-list.html`:__
|
||||
<pre>
|
||||
<ul class="predicates">
|
||||
|
|
@ -170,7 +133,6 @@ __`app/partials/phone-list.html`:__
|
|||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<ul class="phones">
|
||||
<li ng:repeat="phone in phones.$filter(query).$orderBy(orderProp)">
|
||||
<a href="#/phones/{{phone.id}}">{{phone.name}}</a>
|
||||
|
|
@ -180,31 +142,23 @@ __`app/partials/phone-list.html`:__
|
|||
</ul>
|
||||
</pre>
|
||||
|
||||
|
||||
<img src="img/tutorial/tutorial_07_final.png">
|
||||
|
||||
|
||||
We also added a placeholder template for the phone details view:
|
||||
|
||||
|
||||
__`app/partials/phone-list.html`:__
|
||||
<pre>
|
||||
TBD: detail view for {{params.phoneId}}
|
||||
</pre>
|
||||
|
||||
|
||||
Note how we are using `params` model defined in the `PhoneCanCtrl` controller.
|
||||
|
||||
|
||||
|
||||
|
||||
## Test
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
<pre>
|
||||
...
|
||||
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() {
|
||||
|
||||
|
||||
beforeEach(function() {
|
||||
browser().navigateTo('../../app/index.html#/phones/nexus-s');
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
it('should display placeholder page with phoneId', function() {
|
||||
expect(binding('params.phoneId')).toBe('nexus-s');
|
||||
});
|
||||
|
|
@ -231,40 +181,29 @@ to various URLs and verify that the correct view was rendered.
|
|||
</pre>
|
||||
|
||||
|
||||
|
||||
|
||||
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
|
||||
http://angular.github.com/angular-phonecat/step-7/test/e2e/runner.html
|
||||
angular's server}.
|
||||
|
||||
|
||||
|
||||
|
||||
# Experiments
|
||||
|
||||
|
||||
* 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
|
||||
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.
|
||||
|
||||
|
||||
* 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
|
||||
`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
|
||||
inheritance and model property shadowing do some wonders.
|
||||
|
||||
|
||||
# Summary
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
|
||||
|
||||
<ul doc:tutorial-nav="7"></ul>
|
||||
|
||||
|
|
|
|||
|
|
@ -2,43 +2,31 @@
|
|||
@name Tutorial: 8 - More Templating
|
||||
@description
|
||||
|
||||
|
||||
<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
|
||||
phone in the phone list.
|
||||
|
||||
|
||||
|
||||
|
||||
<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
|
||||
is displayed.
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
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
|
||||
GitHub}:
|
||||
|
||||
|
||||
## Data
|
||||
|
||||
|
||||
In addition to `phones.json`, the `app/phones/` directory also contains one json file for each
|
||||
phone:
|
||||
|
||||
|
||||
__`app/phones/nexus-s.json`:__ (sample snippet)
|
||||
<pre>
|
||||
{
|
||||
|
|
@ -62,71 +50,53 @@ __`app/phones/nexus-s.json`:__ (sample snippet)
|
|||
</pre>
|
||||
|
||||
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
|
||||
|
||||
## Controller
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
__`app/js/controller.js`:__
|
||||
<pre>
|
||||
function PhoneDetailCtrl($xhr) {
|
||||
var self = this;
|
||||
|
||||
|
||||
$xhr('GET', 'phones/' + self.params.phoneId + '.json', function(code, response) {
|
||||
self.phone = response;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
//PhoneDetailCtrl.$inject = ['$xhr'];
|
||||
</pre>
|
||||
|
||||
|
||||
To construct the URL for the HTTP request, we use `params.phoneId` extracted from the current route
|
||||
in the `PhoneCatCtrl` controller.
|
||||
|
||||
|
||||
|
||||
|
||||
## Template
|
||||
|
||||
|
||||
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
|
||||
our model into the view.
|
||||
|
||||
|
||||
|
||||
|
||||
__`app/partials/phone-details.html`:__
|
||||
<pre>
|
||||
<img ng:src="{{phone.images[0]}}" class="phone"/>
|
||||
|
||||
|
||||
<h1>{{phone.name}}</h1>
|
||||
|
||||
|
||||
<p>{{phone.description}}</p>
|
||||
|
||||
|
||||
<ul class="phone-thumbs">
|
||||
<li ng:repeat="img in phone.images">
|
||||
<img ng:src="{{img}}"/>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<ul class="specs">
|
||||
<li>
|
||||
<span>Availability and Networks</span>
|
||||
|
|
@ -143,17 +113,13 @@ __`app/partials/phone-details.html`:__
|
|||
</ul>
|
||||
</pre>
|
||||
|
||||
|
||||
<img src="img/tutorial/tutorial_08-09_final.png">
|
||||
|
||||
|
||||
## Test
|
||||
|
||||
|
||||
We wrote a new unit test that is similar to the one we wrote for the `PhoneListCtrl` controller in
|
||||
step 5.
|
||||
|
||||
|
||||
__`test/unit/controllerSpec.js`:__
|
||||
<pre>
|
||||
...
|
||||
|
|
@ -162,46 +128,36 @@ __`test/unit/controllerSpec.js`:__
|
|||
$browser.xhr.expectGET('phones/xyz.json').respond({name:'phone xyz'});
|
||||
ctrl = scope.$new(PhoneDetailCtrl);
|
||||
|
||||
|
||||
expect(ctrl.phone).toBeUndefined();
|
||||
$browser.xhr.flush();
|
||||
|
||||
|
||||
expect(ctrl.phone).toEqual({name:'phone xyz'});
|
||||
});
|
||||
...
|
||||
</pre>
|
||||
|
||||
|
||||
To run the unit tests, execute the `./scripts/test.sh` script and you should see the following
|
||||
output.
|
||||
|
||||
|
||||
Chrome: Runner reset.
|
||||
...
|
||||
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)
|
||||
|
||||
|
||||
|
||||
|
||||
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".
|
||||
|
||||
|
||||
__`test/e2e/scenarios.js`:__
|
||||
<pre>
|
||||
...
|
||||
describe('Phone detail view', function() {
|
||||
|
||||
|
||||
beforeEach(function() {
|
||||
browser().navigateTo('../../app/index.html#/phones/nexus-s');
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
it('should display nexus-s page', function() {
|
||||
expect(binding('phone.name')).toBe('Nexus S');
|
||||
});
|
||||
|
|
@ -210,33 +166,23 @@ __`test/e2e/scenarios.js`:__
|
|||
</pre>
|
||||
|
||||
|
||||
|
||||
|
||||
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
|
||||
http://angular.github.com/angular-phonecat/step-8/test/e2e/runner.html
|
||||
angular's server}.
|
||||
|
||||
|
||||
# Experiments
|
||||
|
||||
|
||||
* Using the {@link
|
||||
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
|
||||
Nexus S details page.
|
||||
|
||||
|
||||
|
||||
|
||||
# Summary
|
||||
|
||||
|
||||
Now that the phone details view is in place, proceed to step 9 to learn how to write your own
|
||||
custom display filter.
|
||||
|
||||
|
||||
|
||||
|
||||
<ul doc:tutorial-nav="8"></ul>
|
||||
|
||||
|
|
|
|||
|
|
@ -2,44 +2,31 @@
|
|||
@name Tutorial: 9 - Filters
|
||||
@description
|
||||
|
||||
|
||||
<ul doc:tutorial-nav="9"></ul>
|
||||
|
||||
|
||||
|
||||
|
||||
In this step you will learn how to create your own custom display filter.
|
||||
|
||||
|
||||
|
||||
|
||||
<doc:tutorial-instructions step="9"></doc:tutorial-instructions>
|
||||
|
||||
|
||||
|
||||
|
||||
Navigate to one of the detail pages.
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
https://github.com/angular/angular-phonecat/compare/step-8...step-9
|
||||
GitHub}:
|
||||
|
||||
|
||||
|
||||
|
||||
## Custom Filter
|
||||
|
||||
|
||||
In order to create a new filter, simply register your custom filter function with the {@link
|
||||
api/angular.filter `angular.filter`} API.
|
||||
|
||||
|
||||
__`app/js/filters.js`:__
|
||||
<pre>
|
||||
angular.filter('checkmark', function(input) {
|
||||
|
|
@ -47,21 +34,16 @@ angular.filter('checkmark', function(input) {
|
|||
});
|
||||
</pre>
|
||||
|
||||
|
||||
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
|
||||
`\u2718`).
|
||||
|
||||
|
||||
|
||||
|
||||
## Template
|
||||
|
||||
|
||||
Since the filter code lives in the `app/js/filters.js` file, we need to include this file in our
|
||||
layout template.
|
||||
|
||||
|
||||
__`app/index.html`:__
|
||||
<pre>
|
||||
...
|
||||
|
|
@ -70,19 +52,14 @@ __`app/index.html`:__
|
|||
...
|
||||
</pre>
|
||||
|
||||
|
||||
The syntax for using filters in angular templates is as follows:
|
||||
|
||||
|
||||
{{ expression | filter }}
|
||||
|
||||
|
||||
Let's employ the filter in the phone details template:
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
__`app/partials/phone-detail.html`:__
|
||||
<pre>
|
||||
...
|
||||
|
|
@ -96,19 +73,14 @@ __`app/partials/phone-detail.html`:__
|
|||
</pre>
|
||||
|
||||
|
||||
|
||||
|
||||
## Test
|
||||
|
||||
|
||||
Filters, like any other component, should be tested and these tests are very easy to write.
|
||||
|
||||
|
||||
__`test/unit/filtersSpec.js`:__
|
||||
<pre>
|
||||
describe('checkmark filter', function() {
|
||||
|
||||
|
||||
it('should convert boolean values to unicode checkmark or cross', function() {
|
||||
expect(angular.filter.checkmark(true)).toBe('\u2713');
|
||||
expect(angular.filter.checkmark(false)).toBe('\u2718');
|
||||
|
|
|
|||
|
|
@ -2,80 +2,59 @@
|
|||
@name Tutorial: 10 - Event Handlers
|
||||
@description
|
||||
|
||||
|
||||
<ul doc:tutorial-nav="10"></ul>
|
||||
|
||||
|
||||
|
||||
|
||||
In this step, you will add a clickable phone image swapper to the phone details page.
|
||||
|
||||
|
||||
|
||||
|
||||
<doc:tutorial-instructions step="10"></doc:tutorial-instructions>
|
||||
|
||||
|
||||
|
||||
|
||||
The phone details view displays one large image of the current phone and several smaller thumbnail
|
||||
images. It would be great if we could replace the large image with any of the thumbnails just by
|
||||
clicking on the desired thumbnail image. Let's have a look at how we can do this with angular.
|
||||
|
||||
|
||||
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
|
||||
GitHub}:
|
||||
|
||||
|
||||
|
||||
|
||||
## Controller
|
||||
|
||||
|
||||
__`app/js/controllers.js`:__
|
||||
<pre>
|
||||
...
|
||||
function PhoneDetailCtrl($xhr) {
|
||||
var self = this;
|
||||
|
||||
|
||||
$xhr('GET', 'phones/' + self.params.phoneId + '.json', function(code, response) {
|
||||
self.phone = response;
|
||||
self.mainImageUrl = response.images[0];
|
||||
});
|
||||
|
||||
|
||||
self.setImage = function(imageUrl) {
|
||||
self.mainImageUrl = imageUrl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//PhoneDetailCtrl.$inject = ['$xhr'];
|
||||
</pre>
|
||||
|
||||
|
||||
In the `PhoneDetailCtrl` controller, we created the `mainImageUrl` model property and set its
|
||||
default value to the first phone image url.
|
||||
|
||||
|
||||
We also created a `setImage` controller method to change the value of `mainImageUrl`.
|
||||
|
||||
|
||||
|
||||
|
||||
## Template
|
||||
|
||||
|
||||
__`app/partials/phone-detail.html`:__
|
||||
<pre>
|
||||
<img ng:src="{{mainImageUrl}}" class="phone"/>
|
||||
|
||||
|
||||
...
|
||||
|
||||
|
||||
<ul class="phone-thumbs">
|
||||
<li ng:repeat="img in phone.images">
|
||||
<img ng:src="{{img}}" ng:click="setImage(img)">
|
||||
|
|
@ -84,53 +63,40 @@ __`app/partials/phone-detail.html`:__
|
|||
...
|
||||
</pre>
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
image.
|
||||
|
||||
|
||||
<img src="img/tutorial/tutorial_10-11_final.png">
|
||||
|
||||
|
||||
|
||||
## Test
|
||||
|
||||
|
||||
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
|
||||
verifies that the main image changed appropriately.
|
||||
|
||||
|
||||
__`test/e2e/scenarios.js`:__
|
||||
<pre>
|
||||
...
|
||||
describe('Phone detail view', function() {
|
||||
|
||||
|
||||
beforeEach(function() {
|
||||
browser().navigateTo('../../app/index.html#/phones/nexus-s');
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
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');
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
it('should swap main image if a thumbnail image is clicked on', function() {
|
||||
element('.phone-thumbs li:nth-child(3) img').click();
|
||||
expect(element('img.phone').attr('src')).toBe('img/phones/nexus-s.2.jpg');
|
||||
|
||||
|
||||
element('.phone-thumbs li:nth-child(1) img').click();
|
||||
expect(element('img.phone').attr('src')).toBe('img/phones/nexus-s.0.jpg');
|
||||
});
|
||||
|
|
@ -138,51 +104,37 @@ __`test/e2e/scenarios.js`:__
|
|||
});
|
||||
</pre>
|
||||
|
||||
|
||||
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
|
||||
http://angular.github.com/angular-phonecat/step-8/test/e2e/runner.html
|
||||
angular's server}.
|
||||
|
||||
|
||||
# Experiments
|
||||
|
||||
|
||||
* Let's add a new controller method to `PhoneCatCtrl`:
|
||||
|
||||
|
||||
this.hello(name) = function(name) {
|
||||
alert('Hello ' + (name || 'world') + '!');
|
||||
}
|
||||
|
||||
|
||||
and add:
|
||||
|
||||
|
||||
<button ng:click="hello('Elmo')">Hello</button>
|
||||
|
||||
|
||||
to the `index.html` template.
|
||||
|
||||
|
||||
The controller methods are inherited between controllers/scopes, so you can use the same snippet
|
||||
in the `phone-list.html` template as well.
|
||||
|
||||
|
||||
* 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`
|
||||
template remains operational.
|
||||
|
||||
|
||||
|
||||
|
||||
# Summary
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
|
||||
|
||||
<ul doc:tutorial-nav="10"></ul>
|
||||
|
|
|
|||
|
|
@ -2,42 +2,30 @@
|
|||
@name Tutorial: 11 - REST and Custom Services
|
||||
@description
|
||||
|
||||
|
||||
<ul doc:tutorial-nav="11"></ul>
|
||||
|
||||
|
||||
|
||||
|
||||
In this step, you will improve the way our app fetches data.
|
||||
|
||||
|
||||
|
||||
|
||||
<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
|
||||
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
|
||||
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
|
||||
https://github.com/angular/angular-phonecat/compare/step-10...step-11
|
||||
GitHub}:
|
||||
|
||||
|
||||
|
||||
|
||||
## Template
|
||||
|
||||
|
||||
The custom service is defined in `app/js/services.js` so we need to include this file in our layout
|
||||
template:
|
||||
|
||||
|
||||
__`app/index.html`.__
|
||||
<pre>
|
||||
...
|
||||
|
|
@ -45,10 +33,8 @@ __`app/index.html`.__
|
|||
...
|
||||
</pre>
|
||||
|
||||
|
||||
## Service
|
||||
|
||||
|
||||
__`app/js/services.js`.__
|
||||
<pre>
|
||||
angular.service('Phone', function($resource) {
|
||||
|
|
@ -58,36 +44,29 @@ __`app/js/services.js`.__
|
|||
});
|
||||
</pre>
|
||||
|
||||
|
||||
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
|
||||
constructor in that both can declare dependencies via function arguments. The Phone service
|
||||
declared a dependency on the `$resource` service.
|
||||
|
||||
|
||||
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
|
||||
of code. This client can then be used in our application, instead of the lower-level {@link
|
||||
api/angular.service.$xhr $xhr} service.
|
||||
|
||||
|
||||
|
||||
|
||||
## Controller
|
||||
|
||||
|
||||
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
|
||||
`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
|
||||
resources. It is also easier now to understand what the code in our controllers is doing.
|
||||
|
||||
|
||||
__`app/js/controllers.js`.__
|
||||
<pre>
|
||||
...
|
||||
|
||||
|
||||
function PhoneListCtrl(Phone_) {
|
||||
this.orderProp = 'age';
|
||||
this.phones = Phone_.query();
|
||||
|
|
@ -95,40 +74,30 @@ function PhoneListCtrl(Phone_) {
|
|||
//PhoneListCtrl.$inject = ['Phone'];
|
||||
|
||||
|
||||
|
||||
|
||||
function PhoneDetailCtrl(Phone_) {
|
||||
var self = this;
|
||||
|
||||
|
||||
self.phone = Phone_.get({phoneId: self.params.phoneId}, function(phone) {
|
||||
self.mainImageUrl = phone.images[0];
|
||||
});
|
||||
|
||||
|
||||
...
|
||||
}
|
||||
//PhoneDetailCtrl.$inject = ['Phone'];
|
||||
</pre>
|
||||
|
||||
|
||||
Notice how in `PhoneListCtrl` we replaced:
|
||||
|
||||
|
||||
$xhr('GET', 'phones/phones.json', function(code, response) {
|
||||
self.phones = response;
|
||||
});
|
||||
|
||||
|
||||
with:
|
||||
|
||||
|
||||
this.phones = Phone_.query();
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
|
|
@ -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
|
||||
view will automatically update.
|
||||
|
||||
|
||||
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
|
||||
`PhoneDetailCtrl` controller illustrates this by setting the `mainImageUrl` in a callback.
|
||||
|
||||
|
||||
|
||||
|
||||
## Test
|
||||
|
||||
|
||||
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
|
||||
service correctly.
|
||||
|
||||
|
||||
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,
|
||||
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.
|
||||
|
||||
|
||||
|
||||
|
||||
__`test/unit/controllersSpec.js`:__
|
||||
<pre>
|
||||
describe('PhoneCat controllers', function() {
|
||||
|
||||
|
||||
beforeEach(function(){
|
||||
this.addMatchers({
|
||||
toEqualData: function(expected) {
|
||||
|
|
@ -176,92 +137,71 @@ describe('PhoneCat controllers', function() {
|
|||
});
|
||||
});
|
||||
|
||||
|
||||
describe('PhoneListCtrl', function() {
|
||||
var scope, $browser, ctrl;
|
||||
|
||||
|
||||
beforeEach(function() {
|
||||
scope = angular.scope();
|
||||
$browser = scope.$service('$browser');
|
||||
|
||||
|
||||
$browser.xhr.expectGET('phones/phones.json')
|
||||
.respond([{name: 'Nexus S'}, {name: 'Motorola DROID'}]);
|
||||
ctrl = scope.$new(PhoneListCtrl);
|
||||
});
|
||||
|
||||
|
||||
it('should create "phones" model with 2 phones fetched from xhr', function() {
|
||||
expect(ctrl.phones).toEqual([]);
|
||||
$browser.xhr.flush();
|
||||
|
||||
|
||||
expect(ctrl.phones).toEqualData([{name: 'Nexus S'},
|
||||
{name: 'Motorola DROID'}]);
|
||||
});
|
||||
|
||||
|
||||
it('should set the default value of orderProp model', function() {
|
||||
expect(ctrl.orderProp).toBe('age');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
describe('PhoneDetailCtrl', function() {
|
||||
var scope, $browser, ctrl;
|
||||
|
||||
|
||||
beforeEach(function() {
|
||||
scope = angular.scope();
|
||||
$browser = scope.$service('$browser');
|
||||
});
|
||||
|
||||
|
||||
beforeEach(function() {
|
||||
scope = angular.scope();
|
||||
$browser = scope.$service('$browser');
|
||||
});
|
||||
|
||||
|
||||
it('should fetch phone detail', function() {
|
||||
scope.params = {phoneId:'xyz'};
|
||||
$browser.xhr.expectGET('phones/xyz.json').respond({name:'phone xyz'});
|
||||
ctrl = scope.$new(PhoneDetailCtrl);
|
||||
|
||||
|
||||
expect(ctrl.phone).toEqualData({});
|
||||
$browser.xhr.flush();
|
||||
|
||||
|
||||
expect(ctrl.phone).toEqualData({name:'phone xyz'});
|
||||
});
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
|
||||
|
||||
To run the unit tests, execute the `./scripts/test.sh` script and you should see the following
|
||||
output.
|
||||
|
||||
|
||||
Chrome: Runner reset.
|
||||
....
|
||||
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)
|
||||
|
||||
|
||||
|
||||
|
||||
# Summary
|
||||
|
||||
|
||||
There you have it! We have created a web app in a relatively short amount of time.
|
||||
|
||||
|
||||
|
||||
|
||||
<ul doc:tutorial-nav="11"></ul>
|
||||
|
||||
|
|
|
|||
|
|
@ -2,26 +2,20 @@
|
|||
@name Tutorial: The End
|
||||
@description
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
For more details and examples of the angular concepts we touched on in this tutorial, see the
|
||||
{@link guide/ Developer Guide}.
|
||||
|
||||
|
||||
For several more examples of code, see the {@link cookbook/ Cookbook}.
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
https://groups.google.com/forum/#!forum/angular}.
|
||||
|
|
|
|||
Loading…
Reference in a new issue