mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-16 23:30:23 +00:00
new batch of docs
This commit is contained in:
parent
5533e48dea
commit
7f1e2e4846
76 changed files with 5363 additions and 931 deletions
|
|
@ -1,4 +0,0 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name angular
|
||||
@namespace The exported angular namespace.
|
||||
|
|
@ -1,175 +1,32 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name angular.service
|
||||
|
||||
@description
|
||||
# Overview
|
||||
Services are substituable objects, which are wired together using dependency injection (DI).
|
||||
Each service could have dependencies (other services), which are passed in constructor.
|
||||
Because JS is dynamicaly typed language, dependency injection can not use static types
|
||||
to identify these dependencies, so each service must explicitely define its dependencies.
|
||||
This is done by `$inject` property.
|
||||
|
||||
|
||||
# Built-in services
|
||||
angular provides a set of services for common operations. These services can be overriden by custom
|
||||
services if needed.
|
||||
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}.
|
||||
|
||||
Like other core angular variables and identifiers, the built-in services always start with `$`.
|
||||
|
||||
* {@link angular.service.$browser $browser}
|
||||
* {@link angular.service.$window $window}
|
||||
* {@link angular.service.$document $document}
|
||||
* {@link angular.service.$location $location}
|
||||
* {@link angular.service.$log $log}
|
||||
* {@link angular.service.$exceptionHandler $exceptionHandler}
|
||||
* {@link angular.service.$hover $hover}
|
||||
* {@link angular.service.$invalidWidgets $invalidWidgets}
|
||||
* {@link angular.service.$route $route}
|
||||
* {@link angular.service.$xhr $xhr}
|
||||
* {@link angular.service.$xhr.error $xhr.error}
|
||||
* {@link angular.service.$xhr.bulk $xhr.bulk}
|
||||
* {@link angular.service.$xhr.cache $xhr.cache}
|
||||
* {@link angular.service.$resource $resource}
|
||||
* {@link angular.service.$cookies $cookies}
|
||||
* {@link angular.service.$cookieStore $cookieStore}
|
||||
* {@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
|
||||
* {@link angular.service.$cookies $cookies } - Provides read / write access to browser cookies
|
||||
* {@link angular.service.$defer $defer } - Defers function execution and try / catch block
|
||||
* {@link angular.service.$document $document } - Provides reference to `window.document` element
|
||||
* {@link angular.service.$exceptionHandler $exceptionHandler } - Receives uncaught angular
|
||||
exceptions
|
||||
* {@link angular.service.$hover $hover } -
|
||||
* {@link angular.service.$invalidWidgets $invalidWidgets } - Holds references to invalid widgets
|
||||
* {@link angular.service.$location $location } - Parses the browser location URL
|
||||
* {@link angular.service.$log $log } - Provides logging service
|
||||
* {@link angular.service.$resource $resource } - Creates objects for interacting with RESTful
|
||||
server-side data sources
|
||||
* {@link angular.service.$route $route } - Provides deep-linking services
|
||||
* {@link angular.service.$updateView $updateView } - Queues view updates
|
||||
* {@link angular.service.$window $window } - References the browsers `window` object
|
||||
* {@link angular.service.$xhr $xhr} - Generates an XHR request.
|
||||
|
||||
# Writing your own custom services
|
||||
angular provides only set of basic services, so for any nontrivial application it will be necessary
|
||||
to write one or more custom services. To do so, a factory function that creates a services needs to
|
||||
be registered with angular's dependency injector. This factory function must return an object - the
|
||||
service (it is not called with the `new` operator).
|
||||
|
||||
**angular.service** accepts three parameters:
|
||||
|
||||
- `{string} name` - Name of the service.
|
||||
- `{function()} factory` - Factory function (called just once by DI).
|
||||
- `{Object} config` - Configuration object with following properties:
|
||||
- `$inject` - {Array.<string>} - Array of service ids that this service depends on. These
|
||||
services will be passed as arguments into the factory function in the same order as specified
|
||||
in the `$inject` array. Defaults to `[]`.
|
||||
- `$eager` - {boolean} - If true, the service factory will be called and thus, the service will
|
||||
be instantiated when angular boots. If false, 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.
|
||||
|
||||
angular enables services to participate in 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.
|
||||
|
||||
Here is an example of very simple service. This service requires $window service (it's
|
||||
passed as a parameter to factory function) and it's just a function.
|
||||
|
||||
This service simple stores all notifications and after third one, it displays all of them by
|
||||
window alert.
|
||||
<pre>
|
||||
angular.service('notify', function(win) {
|
||||
var msgs = [];
|
||||
return function(msg) {
|
||||
msgs.push(msg);
|
||||
if (msgs.length == 3) {
|
||||
win.alert(msgs.join("\n"));
|
||||
msgs = [];
|
||||
}
|
||||
};
|
||||
}, {$inject: ['$window']});
|
||||
</pre>
|
||||
|
||||
And here is a unit test for this service. We use Jasmine spy (mock) instead of real browser's 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');
|
||||
notify('three');
|
||||
expect(mock.alert).toHaveBeenCalledWith("one\ntwo\nthree");
|
||||
});
|
||||
|
||||
it('should clear messages after alert', function() {
|
||||
notify('one');
|
||||
notify('two');
|
||||
notify('third');
|
||||
notify('more');
|
||||
notify('two');
|
||||
notify('third');
|
||||
expect(mock.alert.callCount).toEqual(2);
|
||||
expect(mock.alert.mostRecentCall.args).toEqual(["more\ntwo\nthird"]);
|
||||
});
|
||||
</pre>
|
||||
|
||||
# Injecting services into controllers
|
||||
Using services as dependencies for controllers is very similar to using them as dependencies for
|
||||
another service.
|
||||
|
||||
JavaScript is dynamic language, so DI is not able to figure out which services to inject by
|
||||
static types (like in static typed languages). Therefore you must specify the service name
|
||||
by the `$inject` property - it's an array that contains strings with names of services to be
|
||||
injected. The name must match the id that service has been registered as with angular.
|
||||
The order of the services in the array matters, because this order will be used when calling
|
||||
the factory function 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() {
|
||||
// use $location service
|
||||
$loc.setHash();
|
||||
};
|
||||
this.secondMethod = function() {
|
||||
// use $log service
|
||||
$log.info('...');
|
||||
};
|
||||
}
|
||||
// which services to inject ?
|
||||
myController.$inject = ['$location', '$log'];
|
||||
</pre>
|
||||
|
||||
@example
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script type="text/javascript">
|
||||
angular.service('notify', function(win) {
|
||||
var msgs = [];
|
||||
return function(msg) {
|
||||
msgs.push(msg);
|
||||
if (msgs.length == 3) {
|
||||
win.alert(msgs.join("\n"));
|
||||
msgs = [];
|
||||
}
|
||||
};
|
||||
}, {$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" />
|
||||
<button ng:click="callNotify(message);">NOTIFY</button>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should test service', function(){
|
||||
expect(element(':input[name=message]').val()).toEqual('test');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
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.
|
||||
|
|
|
|||
78
docs/content/api/index.ngdoc
Normal file
78
docs/content/api/index.ngdoc
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@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.filter Filters} - Angular filter functions
|
||||
* {@link angular.formatter Formatters} - Angular formatting functions
|
||||
* {@link angular.validator Validators} - Angular validators
|
||||
|
||||
|
||||
|
||||
|
||||
## Angular Scope API
|
||||
|
||||
|
||||
* {@link angular.scope Scope Object} - Angular scope object functions
|
||||
|
||||
|
||||
|
||||
|
||||
## Angular Services API
|
||||
|
||||
|
||||
* {@link angular.service Angular Services}
|
||||
|
||||
|
||||
|
||||
|
||||
## 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 Utilities API
|
||||
|
||||
|
||||
* {@link angular.bind angular.bind() }
|
||||
* {@link angular.compile angular.compile() }
|
||||
* {@link angular.extend angular.extend() }
|
||||
* {@link angular.forEach angular.forEach() }
|
||||
* {@link angular.fromJson angular.fromJson() }
|
||||
* {@link angular.identity angular.identity() }
|
||||
* {@link angular.injector angular.injector() }
|
||||
* {@link angular.isArray angular.isArray() }
|
||||
* {@link angular.isDate angular.isDate() }
|
||||
* {@link angular.isDefined angular.isDefined() }
|
||||
* {@link angular.isFunction angular.isFunction() }
|
||||
* {@link angular.isNumber angular.isNumber() }
|
||||
* {@link angular.isObject angular.isObject() }
|
||||
* {@link angular.isString angular.isString() }
|
||||
* {@link angular.isUndefined angular.isUndefined() }
|
||||
* {@link angular.lowercase angular.lowercase() }
|
||||
* {@link angular.noop angular.noop() }
|
||||
* {@link angular.toJson angular.toJson() }
|
||||
* {@link angular.uppercase angular.uppercase() }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Augmented JavaScript Objects
|
||||
|
||||
|
||||
* {@link angular.object Object API} - Utility functions for JavaScript objects
|
||||
* {@link angular.array Array API} - Utility functions for JavaScript arrays
|
||||
123
docs/content/cookbook/advancedform.ngdoc
Normal file
123
docs/content/cookbook/advancedform.ngdoc
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@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>
|
||||
UserForm.$inject = ['$invalidWidgets'];
|
||||
function UserForm($invalidWidgets){
|
||||
this.$invalidWidgets = $invalidWidgets;
|
||||
this.state = /^\w\w$/;
|
||||
this.zip = /^\d\d\d\d\d$/;
|
||||
this.master = {
|
||||
name: 'John Smith',
|
||||
address:{
|
||||
line1: '123 Main St.',
|
||||
city:'Anytown',
|
||||
state:'AA',
|
||||
zip:'12345'
|
||||
},
|
||||
contacts:[
|
||||
{type:'phone', value:'1(234) 555-1212'}
|
||||
]
|
||||
};
|
||||
this.cancel();
|
||||
}
|
||||
|
||||
|
||||
UserForm.prototype = {
|
||||
cancel: function(){
|
||||
this.form = angular.copy(this.master);
|
||||
},
|
||||
|
||||
|
||||
save: function(){
|
||||
this.master = this.form;
|
||||
this.cancel();
|
||||
}
|
||||
};
|
||||
</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/>,
|
||||
<input type="text" name="form.address.state" size="2" ng:required ng:validate="regexp:state"/>
|
||||
<input type="text" name="form.address.zip" size="5" ng:required
|
||||
ng:validate="regexp:zip"/><br/><br/>
|
||||
|
||||
|
||||
<label>Phone:</label>
|
||||
[ <a href="" ng:click="form.contacts.$add()">add</a> ]
|
||||
<div ng:repeat="contact in form.contacts">
|
||||
<select name="contact.type">
|
||||
<option>email</option>
|
||||
<option>phone</option>
|
||||
<option>pager</option>
|
||||
<option>IM</option>
|
||||
</select>
|
||||
<input type="text" name="contact.value" ng:required/>
|
||||
[ <a href="" ng:click="form.contacts.$remove(contact)">X</a> ]
|
||||
</div>
|
||||
<button ng:click="cancel()" disabled="{{master.$equals(form)}}">Cancel</button>
|
||||
<button ng:click="save()" disabled="{{$invalidWidgets.visible() ||
|
||||
master.$equals(form)}}">Save</button>
|
||||
|
||||
|
||||
<hr/>
|
||||
Debug View:
|
||||
<pre>form={{form}}
|
||||
master={{master}}</pre>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should enable save button', function(){
|
||||
expect(element(':button:contains(Save)').attr('disabled')).toBeTruthy();
|
||||
input('form.name').enter('');
|
||||
expect(element(':button:contains(Save)').attr('disabled')).toBeTruthy();
|
||||
input('form.name').enter('change');
|
||||
expect(element(':button:contains(Save)').attr('disabled')).toBeFalsy();
|
||||
element(':button:contains(Save)').click();
|
||||
expect(element(':button:contains(Save)').attr('disabled')).toBeTruthy();
|
||||
});
|
||||
it('should enable cancel button', function(){
|
||||
expect(element(':button:contains(Cancel)').attr('disabled')).toBeTruthy();
|
||||
input('form.name').enter('change');
|
||||
expect(element(':button:contains(Cancel)').attr('disabled')).toBeFalsy();
|
||||
element(':button:contains(Cancel)').click();
|
||||
expect(element(':button:contains(Cancel)').attr('disabled')).toBeTruthy();
|
||||
expect(element(':input[name=form.name]').val()).toEqual('John Smith');
|
||||
});
|
||||
</doc:scenario>
|
||||
</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.
|
||||
* Cancel reverts the form changes back to original state.
|
||||
* Save updates the internal model of the form.
|
||||
* Debug view shows the two models. One presented to the user form and the other being the pristine
|
||||
copy master.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -3,12 +3,15 @@
|
|||
@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
|
||||
{@link http://code.google.com/apis/buzz/v1/getting_started.html#background-operations| Google Buzz API}
|
||||
{@link http://code.google.com/apis/buzz/v1/getting_started.html#background-operations| Google Buzz
|
||||
API}
|
||||
to retrieve Buzz activity and comments.
|
||||
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script>
|
||||
|
|
@ -40,7 +43,7 @@ to retrieve Buzz activity and comments.
|
|||
<img src="{{item.actor.thumbnailUrl}}" style="max-height:30px;max-width:30px;"/>
|
||||
<a href="{{item.actor.profileUrl}}">{{item.actor.name}}</a>
|
||||
<a href="" ng:click="expandReplies(item)" style="float: right;">
|
||||
Expand replies: {{item.links.replies[0].count}}
|
||||
Expand replies: {{item.links.replies.count}}
|
||||
</a>
|
||||
</h1>
|
||||
{{item.object.content | html}}
|
||||
|
|
|
|||
|
|
@ -3,14 +3,18 @@
|
|||
@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,
|
||||
|
|
@ -21,23 +25,30 @@ 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>
|
||||
|
|
@ -48,6 +59,7 @@ 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',
|
||||
|
|
@ -55,6 +67,7 @@ The two partials are defined in the following URLs:
|
|||
};
|
||||
}
|
||||
|
||||
|
||||
function WelcomeCntl($route){}
|
||||
WelcomeCntl.prototype = {
|
||||
greet: function(){
|
||||
|
|
@ -62,6 +75,7 @@ The two partials are defined in the following URLs:
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
function SettingsCntl(){
|
||||
this.cancel();
|
||||
}
|
||||
|
|
@ -70,6 +84,7 @@ 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 = "#";
|
||||
|
|
@ -102,13 +117,19 @@ 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 angular.service.$route $route} service with the proper URL routes.
|
||||
* The {@link angular.service.$route $route} service then watches the URL and instantiates the
|
||||
initialization of the {@link api/angular.service.$route $route} service with the proper URL
|
||||
routes.
|
||||
* The {@link api/angular.service.$route $route} service then watches the URL and instantiates the
|
||||
appropriate controller when the URL changes.
|
||||
* The {@link angular.widget.ng:view ng:view} widget loads the view when the URL changes. It also
|
||||
* The {@link api/angular.widget.ng:view ng:view} widget loads the view when the URL changes. It
|
||||
also
|
||||
sets the view scope to the newly instantiated controller.
|
||||
* Changing the URL is sufficient to change the controller and view. It makes no difference whether
|
||||
the URL is changed programatically or by the user.
|
||||
|
|
|
|||
|
|
@ -3,11 +3,14 @@
|
|||
@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>
|
||||
|
|
@ -23,14 +26,18 @@ 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/>,
|
||||
<input type="text" name="user.address.state" size="2" ng:required ng:validate="regexp:state"/>
|
||||
<input type="text" name="user.address.zip" size="5" ng:required ng:validate="regexp:zip"/><br/><br/>
|
||||
<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> ]
|
||||
|
|
@ -49,6 +56,7 @@ allow a user to enter data.
|
|||
<pre>user={{user}}</pre>
|
||||
</div>
|
||||
|
||||
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should show debug', function(){
|
||||
|
|
@ -61,42 +69,53 @@ allow a user to enter data.
|
|||
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/);
|
||||
.not().toMatch(/ng-validation-error/)
|
||||
|
||||
|
||||
using('.example').input('user.address.zip').enter('abc');
|
||||
|
||||
|
||||
expect(using('.example').element(':input[name=user.address.zip]').attr('className'))
|
||||
.toMatch(/ng-validation-error/);
|
||||
.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/);
|
||||
.not().toMatch(/ng-validation-error/)
|
||||
|
||||
|
||||
using('.example').input('user.address.state').enter('XXX');
|
||||
|
||||
|
||||
expect(using('.example').element(':input[name=user.address.state]').attr('className'))
|
||||
.toMatch(/ng-validation-error/);
|
||||
.toMatch(/ng-validation-error/)
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
|
||||
|
||||
|
||||
# Things to notice
|
||||
|
||||
* The user data model is initialized {@link angular.directive.@ng:controller controller} and is available in
|
||||
the {@link angular.scope scope} with the initial data.
|
||||
|
||||
* 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.
|
||||
* For debugging purposes we have included a debug view of the model to better understand what
|
||||
is going on.
|
||||
* The {@link angular.widget.HTML input widgets} simply refer to the model and are auto bound.
|
||||
* The inputs {@link angular.validator validate}. (Try leaving them blank or entering non digits
|
||||
* The {@link api/angular.widget.HTML input widgets} simply refer to the model and are auto bound.
|
||||
* The inputs {@link api/angular.validator validate}. (Try leaving them blank or entering non digits
|
||||
in the zip field)
|
||||
* In your application you can simply read from or write to the model and the form will be updated.
|
||||
* By clicking the 'add' link you are adding new items into the `user.contacts` array which are then
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
@name Cookbook: Hello World
|
||||
@description
|
||||
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
Your name: <input type="text" name="name" value="World"/>
|
||||
|
|
@ -18,14 +19,18 @@
|
|||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
|
||||
# Things to notice
|
||||
|
||||
|
||||
Take a look through the source and note:
|
||||
|
||||
* The script tag that {@link guide.bootstrap bootstraps} the angular environment.
|
||||
* The text {@link angular.widget.HTML input widget} which is bound to the greeting name text.
|
||||
|
||||
* 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.
|
||||
* The implicit presence of the `name` variable which is in the root {@link angular.scope scope}.
|
||||
* The implicit presence of the `name` variable which is in the root {@link api/angular.scope scope}.
|
||||
* The double curly brace `{{markup}}`, which binds the name variable to the greeting text.
|
||||
* The concept of {@link guide.data-binding data binding}, which reflects any changes to the
|
||||
* The concept of {@link guide/dev_guide.templates.databinding data binding}, which reflects any
|
||||
changes to the
|
||||
input field in the greeting text.
|
||||
|
|
|
|||
|
|
@ -3,58 +3,80 @@
|
|||
@name Cookbook
|
||||
@description
|
||||
|
||||
|
||||
Welcome to the angular cookbook. Here we will show you typical uses of angular by example.
|
||||
|
||||
|
||||
|
||||
|
||||
# Hello World
|
||||
|
||||
{@link cookbook.helloworld Hello World}: The simplest possible application that demonstrates the
|
||||
|
||||
{@link helloworld Hello World}: The simplest possible application that demonstrates the
|
||||
classic Hello World!
|
||||
|
||||
|
||||
|
||||
|
||||
# Basic Form
|
||||
|
||||
{@link cookbook.form Basic Form}: Displaying forms to the user for editing is the bread and butter
|
||||
|
||||
{@link form Basic Form}: Displaying forms to the user for editing is the bread and butter
|
||||
of web applications. Angular makes forms easy through bidirectional data binding.
|
||||
|
||||
|
||||
|
||||
|
||||
# Advanced Form
|
||||
|
||||
{@link cookbook.formadvanced Advanced Form}: Taking the form example to the next level and
|
||||
|
||||
{@link advancedform Advanced Form}: Taking the form example to the next level and
|
||||
providing advanced features such as dirty detection, form reverting and submit disabling if
|
||||
validation errors exist.
|
||||
|
||||
|
||||
|
||||
|
||||
# Model View Controller
|
||||
|
||||
{@link cookbook.mvc MVC}: Tic-Tac-Toe: Model View Controller (MVC) is a time-tested design pattern
|
||||
|
||||
{@link mvc MVC}: Tic-Tac-Toe: Model View Controller (MVC) is a time-tested design pattern
|
||||
to separate the behavior (JavaScript controller) from the presentation (HTML view). This
|
||||
separation aids in maintainability and testability of your project.
|
||||
|
||||
|
||||
|
||||
|
||||
# Multi-page App and Deep Linking
|
||||
|
||||
{@link cookbook.deeplinking Deep Linking}: An AJAX application never navigates away from the
|
||||
|
||||
{@link deeplinking Deep Linking}: An AJAX application never navigates away from the
|
||||
first page it loads. Instead, it changes the DOM of its single page. Eliminating full-page reloads
|
||||
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 angular.service Services}: Services are long lived objects in your applications that are
|
||||
|
||||
{@link api/angular.service Services}: Services are long lived objects in your applications that are
|
||||
available across controllers. A collection of useful services are pre-bundled with angular but you
|
||||
will likely add your own. Services are initialized using dependency injection, which resolves the
|
||||
order of initialization. This safeguards you from the perils of global state (a common way to
|
||||
implement long lived objects).
|
||||
|
||||
|
||||
|
||||
|
||||
# External Resources
|
||||
|
||||
{@link cookbook.buzz Resources}: Web applications must be able to communicate with the external
|
||||
|
||||
{@link buzz Resources}: Web applications must be able to communicate with the external
|
||||
services to get and update data. Resources are the abstractions of external URLs which are
|
||||
specially tailored to angular data binding.
|
||||
|
||||
|
|
|
|||
|
|
@ -3,19 +3,24 @@
|
|||
@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>
|
||||
function TicTacToeCntl(){
|
||||
function TicTacToeCntl($location){
|
||||
this.$location = $location;
|
||||
this.cellStyle= {
|
||||
'height': '20px',
|
||||
'width': '20px',
|
||||
|
|
@ -29,8 +34,8 @@ no connection between the controller and the view.
|
|||
}
|
||||
TicTacToeCntl.prototype = {
|
||||
dropPiece: function(row, col) {
|
||||
if (!this.winner && !this.board[row][col]) {
|
||||
this.board[row][col] = this.nextMove;
|
||||
if (!this.winner && !this.board) {
|
||||
this.board = this.nextMove;
|
||||
this.nextMove = this.nextMove == 'X' ? 'O' : 'X';
|
||||
this.setUrl();
|
||||
}
|
||||
|
|
@ -51,9 +56,9 @@ no connection between the controller and the view.
|
|||
row(0) || row(1) || row(2) ||
|
||||
col(0) || col(1) || col(2) ||
|
||||
diagonal(-1) || diagonal(1);
|
||||
function row(r) { return same(b[r][0], b[r][1], b[r][2]);}
|
||||
function col(c) { return same(b[0][c], b[1][c], b[2][c]);}
|
||||
function diagonal(i) { return same(b[0][1-i], b[1][1], b[2][1+i]);}
|
||||
function row(r) { return same(b, b, b);}
|
||||
function col(c) { return same(b, b, b);}
|
||||
function diagonal(i) { return same(b[1-i], b, b[1+i]);}
|
||||
function same(a, b, c) { return (a==b && b==c) ? a : '';};
|
||||
},
|
||||
setUrl: function(){
|
||||
|
|
@ -63,12 +68,12 @@ no connection between the controller and the view.
|
|||
});
|
||||
this.$location.hashSearch.board = rows.join(';') + '/' + this.nextMove;
|
||||
},
|
||||
readUrl: function(value) {
|
||||
readUrl: function(scope, value) {
|
||||
if (value) {
|
||||
value = value.split('/');
|
||||
this.nextMove = value[1];
|
||||
angular.forEach(value[0].split(';'), function(row, i){
|
||||
this.board[i] = row.split(',');
|
||||
this.nextMove = value;
|
||||
angular.forEach(value.split(';'), function(row, i){
|
||||
this.board = row.split(',');
|
||||
}, this);
|
||||
this.grade();
|
||||
} else {
|
||||
|
|
@ -102,6 +107,7 @@ 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();
|
||||
}
|
||||
|
|
@ -109,8 +115,11 @@ 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.
|
||||
|
|
@ -122,4 +131,5 @@ no connection between the controller and the view.
|
|||
* The view can call any controller function.
|
||||
* In this example, the `setUrl()` and `readUrl()` functions copy the game state to/from the URL's
|
||||
hash so the browser's back button will undo game steps. See deep-linking. This example calls
|
||||
{@link angular.scope.$watch $watch()} to set up a listener that invokes `readUrl()` when needed.
|
||||
{@link api/angular.scope.$watch $watch()} to set up a listener that invokes `readUrl()` when
|
||||
needed.
|
||||
|
|
|
|||
130
docs/content/guide/dev_guide.bootstrap.auto_bootstrap.ngdoc
Normal file
130
docs/content/guide/dev_guide.bootstrap.auto_bootstrap.ngdoc
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@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
|
||||
data and their part of the view, and ultimately constructs a runnable application. The resulting
|
||||
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>
|
||||
<head>
|
||||
<script type="text/javascript"
|
||||
src="http://code.angularjs.org/angular.js#autobind"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div xmlns:ng="http://angularjs.org">
|
||||
Hello {{'world'}}!
|
||||
</div>
|
||||
</body>
|
||||
</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`
|
||||
- `angular-x.x.x.min.js`
|
||||
- `angular-x.x.x-xxxxxxxx.js` (dev snapshot)
|
||||
- `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}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@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">
|
||||
<head>
|
||||
<script type="text/javascript" src="http://code.angularjs.org/angular.js"
|
||||
ng:autobind></script>
|
||||
<script type="text/javascript">
|
||||
(angular.element(document).ready(function() {
|
||||
angular.compile(document)();
|
||||
})(document);
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
Hello {{'World'}}!
|
||||
</body>
|
||||
</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}
|
||||
86
docs/content/guide/dev_guide.bootstrap.ngdoc
Normal file
86
docs/content/guide/dev_guide.bootstrap.ngdoc
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@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">
|
||||
<head>
|
||||
...
|
||||
</head>
|
||||
<body>
|
||||
...
|
||||
<script src="angular.js" ng:autobind>
|
||||
</body>
|
||||
</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}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@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;
|
||||
return function(linkElement) {
|
||||
var currentScope = this;
|
||||
currentScope.$watch(expression, function(value) {
|
||||
linkElement.text(value);
|
||||
});
|
||||
};
|
||||
});
|
||||
</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}.
|
||||
61
docs/content/guide/dev_guide.compiler.directives.ngdoc
Normal file
61
docs/content/guide/dev_guide.compiler.directives.ngdoc
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@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
|
||||
attribute value to the `ng:bind` function, which in turn sets up the data binding. On any change to
|
||||
the expression in the model, the view is updated to display the span text with the changed
|
||||
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}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@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"
|
||||
ng:init="a=a+1"
|
||||
ng:bind="person">
|
||||
</li>
|
||||
</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}
|
||||
115
docs/content/guide/dev_guide.compiler.extending_compiler.ngdoc
Normal file
115
docs/content/guide/dev_guide.compiler.extending_compiler.ngdoc
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Angular HTML Compiler: Extending the Angular Compiler
|
||||
@description
|
||||
|
||||
|
||||
Let's say that we want to create a new DOM element called `<my:greeter/>` that displays a greeting.
|
||||
We want this HTML source:
|
||||
|
||||
|
||||
<pre>
|
||||
<div ng:init="salutation='Hello'; name='World'">
|
||||
<my:greeter salutation="salutation" name="name"/>
|
||||
</div>
|
||||
</pre>
|
||||
|
||||
|
||||
to produce this DOM:
|
||||
|
||||
|
||||
<pre>
|
||||
<div ng:init="salutation='Hello'; name='World'">
|
||||
<my:greeter salutation="salutation" name="name"/>
|
||||
<span class="salutation">Hello</span>
|
||||
<span class="name">World</span>!
|
||||
</my:greeter>
|
||||
</div>
|
||||
</pre>
|
||||
|
||||
|
||||
That is, the new `<my:greeter/>` tag's `salutation` and `name` attributes should be transformed by
|
||||
the compiler such that two `<span>` tags display the values of the attributes, with CSS classes
|
||||
applied to the output.
|
||||
|
||||
|
||||
The following code snippet shows how to write a following widget definition that will be processed
|
||||
by the compiler. Note that you have to declare the {@link dev_guide.bootstrap namespace} `my` in
|
||||
the page:
|
||||
|
||||
|
||||
<pre>
|
||||
angular.widget('my:greeter', function(compileElement){
|
||||
var compiler = this;
|
||||
compileElement.css('display', 'block');
|
||||
var salutationExp = compileElement.attr('salutation');
|
||||
var nameExp = compileElement.attr('name');
|
||||
return function(linkElement){
|
||||
var salutationSpan = angular.element('<span class="salutation"></span');
|
||||
var nameSpan = angular.element('<span class="name"></span>');
|
||||
linkElement.append(salutationSpan);
|
||||
linkElement.append(compiler.text(' '));
|
||||
linkElement.append(nameSpan);
|
||||
linkElement.append(compiler.text('!'));
|
||||
this.$watch(salutationExp, function(value){
|
||||
salutationSpan.text(value);
|
||||
});
|
||||
this.$watch(nameExp, function(value){
|
||||
nameSpan.text(value);
|
||||
});
|
||||
};
|
||||
});
|
||||
</pre>
|
||||
|
||||
|
||||
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:
|
||||
1. Add a style element with attribute display: block; to the template DOM so that the browser
|
||||
knows to treat the element as block element for rendering. (Note: because this style element was
|
||||
added on the template compileElement, this style is automatically applied to any clones of the
|
||||
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.
|
||||
2. Add the span elements to the linkElement. (Note: be careful not to add them to the
|
||||
compileElement, because that's the template.)
|
||||
3. Set up watches on the expressions. When an expression changes, copy the data to the
|
||||
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()}
|
||||
120
docs/content/guide/dev_guide.compiler.markup.ngdoc
Normal file
120
docs/content/guide/dev_guide.compiler.markup.ngdoc
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@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
|
||||
<hr/>
|
||||
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;
|
||||
var index = text.indexOf('---');
|
||||
if (index > -1) {
|
||||
var before = compiler.text(text.substring(0, index));
|
||||
var hr = compiler.element('hr');
|
||||
var after = compiler.text(text.substring(index + 3));
|
||||
textNode.after(after);
|
||||
textNode.after(hr);
|
||||
textNode.after(before);
|
||||
textNode.remove();
|
||||
}
|
||||
});
|
||||
</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') {
|
||||
element.addClass(attrValue);
|
||||
}
|
||||
});
|
||||
</pre>
|
||||
|
||||
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
|
||||
* {@link dev_guide.compiler Angular HTML Compiler}
|
||||
|
||||
|
||||
## Related API
|
||||
|
||||
|
||||
* {@link api/angular.compile Compiler API Reference}
|
||||
35
docs/content/guide/dev_guide.compiler.ngdoc
Normal file
35
docs/content/guide/dev_guide.compiler.ngdoc
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@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}
|
||||
* {@link dev_guide.compiler.widgets Understanding Angular Widgets}
|
||||
* {@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}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Angular HTML Compiler: Testing a New DOM Element
|
||||
@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()}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@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
|
||||
function. This compilation process happens automatically when the page is loaded when you specify
|
||||
`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
|
||||
called multiple times, once per cloned DOM element. For example, in the case of the {@link
|
||||
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.
|
||||
4. When the compile function completes, it should return a link function. Aggregate this 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()}
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@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;
|
||||
var exp = compileElement.attr('exp');
|
||||
return function(linkElement) {
|
||||
var currentScope = this;
|
||||
currentScope.$watch(exp, function(value){
|
||||
alert(value);
|
||||
});
|
||||
};
|
||||
});
|
||||
</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>
|
||||
</pre>
|
||||
You can implement `my:watch` attribute like this:
|
||||
<pre>
|
||||
angular.widget('@my:watch', function(expression, compileElement) {
|
||||
var compiler = this;
|
||||
return function(linkElement) {
|
||||
var currentScope = this;
|
||||
currentScope.$watch(expression, function(value){
|
||||
alert(value);
|
||||
});
|
||||
};
|
||||
});
|
||||
</pre>
|
||||
|
||||
|
||||
|
||||
|
||||
# Live Example of a Custom Element Widget
|
||||
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script>
|
||||
angular.widget('my:time', function(compileElement){
|
||||
compileElement.css('display', 'block');
|
||||
return function(linkElement){
|
||||
function update(){
|
||||
linkElement.text('Current time is: ' + new Date());
|
||||
setTimeout(update, 1000);
|
||||
}
|
||||
update();
|
||||
};
|
||||
});
|
||||
</script>
|
||||
<my:time></my:time>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
</doc:scenario>
|
||||
</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}
|
||||
47
docs/content/guide/dev_guide.compiler.widgets.ngdoc
Normal file
47
docs/content/guide/dev_guide.compiler.widgets.ngdoc
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@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
|
||||
is allowed per element. An example of an attribute widget is shown in {@link
|
||||
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}
|
||||
43
docs/content/guide/dev_guide.di.ngdoc
Normal file
43
docs/content/guide/dev_guide.di.ngdoc
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@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}
|
||||
141
docs/content/guide/dev_guide.di.understanding_di.ngdoc
Normal file
141
docs/content/guide/dev_guide.di.understanding_di.ngdoc
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: DI: Understanding DI in Angular
|
||||
@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
|
||||
define those dependencies as a property (`$inject`). With this information, the DI subsystem
|
||||
manages the creation of service objects and the provision of those objects to the components that
|
||||
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.
|
||||
3. The `ng:controller` directive implicitly creates a new child scope, augmented by the application
|
||||
of the `PhoneListCtrl` controller function.
|
||||
4. The Injector identifies the `$xhr` service as `PhoneListCtrl` controller's only dependency.
|
||||
5. The Injector checks if the `$xhr` service has already been instantiated, and if not uses the
|
||||
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();
|
||||
// access the service locator
|
||||
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
|
||||
`_`)
|
||||
* 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
|
||||
minifiers/obfuscators. In the future, we may provide a pre-processor which will scan the source
|
||||
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}
|
||||
66
docs/content/guide/dev_guide.di.using_di_controllers.ngdoc
Normal file
66
docs/content/guide/dev_guide.di.using_di_controllers.ngdoc
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@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
|
||||
$route.when(...);
|
||||
}
|
||||
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">
|
||||
<script src="http://code.angularjs.org/angular.min.js" ng:autobind></script>
|
||||
<body>
|
||||
...
|
||||
</body>
|
||||
</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}
|
||||
270
docs/content/guide/dev_guide.expressions.ngdoc
Normal file
270
docs/content/guide/dev_guide.expressions.ngdoc
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@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.
|
||||
* **No Control Flow Statements:** you cannot do the following from an angular expression:
|
||||
conditionals, loops, or throw.
|
||||
* **Type Augmentation:** the scope expression evaluator augments built-in types.
|
||||
* **Filters:** you can add filters to an expression, for example to convert raw data into a
|
||||
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>
|
||||
1+2={{1+2}}
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should calculate expression in binding', function(){
|
||||
expect(binding('1+2')).toEqual('3');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
|
||||
You can try evaluating different expressions here:
|
||||
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<div ng:init="exprs=[]" class="expressions">
|
||||
Expression:
|
||||
<input type='text' name="expr" value="3*10|currency" size="80"/>
|
||||
<button ng:click="exprs.$add(expr)">Evaluate</button>
|
||||
<ul>
|
||||
<li ng:repeat="expr in exprs">
|
||||
[ <a href="" ng:click="exprs.$remove(expr)">X</a> ]
|
||||
<tt>{{expr}}</tt> => <span ng:bind="$parent.$eval(expr)"></span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should allow user expression testing', function(){
|
||||
element('.expressions :button').click();
|
||||
var li = using('.expressions ul').repeater('li');
|
||||
expect(li.count()).toBe(1);
|
||||
expect(li.row(0)).toEqual(["3*10|currency", "$30.00"]);
|
||||
});
|
||||
</doc:scenario>
|
||||
</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')">
|
||||
Name: <input name="name" type="text" value="World"/>
|
||||
<button ng:click="($window.mockWindow || $window).alert('Hello ' + name)">Greet</button>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should calculate expression in binding', function(){
|
||||
var alertText;
|
||||
this.addFutureAction('set mock', function($window, $document, done) {
|
||||
$window.mockWindow = {
|
||||
alert: function(text){ alertText = text; }
|
||||
};
|
||||
done();
|
||||
});
|
||||
element(':button:contains(Greet)').click();
|
||||
expect(this.addFuture('alert text', function(done) {
|
||||
done(null, alertText);
|
||||
})).toBe('Hello World');
|
||||
});
|
||||
</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 = [
|
||||
{name:'John', phone:'555-1212'},
|
||||
{name:'Mary', phone:'555-9876'},
|
||||
{name:'Mike', phone:'555-4321'},
|
||||
{name:'Adam', phone:'555-5678'},
|
||||
{name:'Julie', phone:'555-8765'}]"></div>
|
||||
Search: <input name="searchText"/>
|
||||
<table class="example3">
|
||||
<tr><th>Name</th><th>Phone</th><tr>
|
||||
<tr ng:repeat="friend in friends.$filter(searchText)">
|
||||
<td>{{friend.name}}</td>
|
||||
<td>{{friend.phone}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should filter the list', function(){
|
||||
var tr = using('table.example3').repeater('tr.ng-attr-widget');
|
||||
expect(tr.count()).toBe(5);
|
||||
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
|
||||
objects with additional behavior. By prefixing its additions with $ we are reserving our namespace
|
||||
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}
|
||||
53
docs/content/guide/dev_guide.introduction.ngdoc
Normal file
53
docs/content/guide/dev_guide.introduction.ngdoc
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@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)
|
||||
* Dependency Injection (used primarily for wiring together services)
|
||||
* 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
|
||||
existing round-trip app. You might follow this course of action if you were migrating an older app
|
||||
to a single-page angular app.
|
||||
33
docs/content/guide/dev_guide.mvc.ngdoc
Normal file
33
docs/content/guide/dev_guide.mvc.ngdoc
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@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}
|
||||
|
||||
|
||||
|
||||
323
docs/content/guide/dev_guide.mvc.understanding_controller.ngdoc
Normal file
323
docs/content/guide/dev_guide.mvc.understanding_controller.ngdoc
Normal file
|
|
@ -0,0 +1,323 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@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
|
||||
the template/view, and can be invoked via angular expressions and `ng:` event handlers (e.g. {@link
|
||||
api/angular.directive.ng:click ng:click}). These controller methods are always evaluated within the
|
||||
context of the angular scope object that the controller function was applied to (which means that
|
||||
the `this` keyword of any controller method is always bound to the scope that the controller
|
||||
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
|
||||
logic. Angular offers {@link dev_guide.templates.databinding} for automatic DOM manipulation. If
|
||||
you have to perform your own manual DOM manipulation, encapsulate the presentation logic in {@link
|
||||
dev_guide.compiler.widgets widgets} and {@link dev_guide.compiler.directives directives}.
|
||||
- Input formatting — Use {@link dev_guide.templates.formatters angular formatters} instead.
|
||||
- Output filtering — Use {@link dev_guide.templates.filters angular filters} instead.
|
||||
- Run stateless or stateful code shared across controllers — Use {@link dev_guide.services angular
|
||||
services} instead.
|
||||
- Instantiate or manage the life-cycle of other components (for example, to create service
|
||||
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>
|
||||
<button ng:click="jalapenoSpicy()">Jalapeño</button>
|
||||
<p>The food is {{spice}} spicy!</p>
|
||||
</body>
|
||||
|
||||
|
||||
function SpicyCtrl() {
|
||||
this.spice = 'very';
|
||||
this.chiliSpicy = function() {
|
||||
this.spice = 'chili';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
starts with capital letter and ends with "Ctrl" or "Controller".
|
||||
- The JavaScript keyword `this` in the `SpicyCtrl` function is bound to the scope that the
|
||||
controller augments.
|
||||
- Assigning a property to `this` creates or updates the model.
|
||||
- Controller methods can be created through direct assignment to scope (the `chiliSpicy` method) or
|
||||
as prototype methods of the controller constructor function (the `jalapenoSpicy` method)
|
||||
- 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">
|
||||
<button ng:click="spicy('chili')">Chili</button>
|
||||
<button ng:click="spicy(customSpice)">Custom spice</button>
|
||||
<p>The food is {{spice}} spicy!</p>
|
||||
</body>
|
||||
|
||||
|
||||
function SpicyCtrl() {
|
||||
this.spice = 'very';
|
||||
this.spicy = function(spice) {
|
||||
this.spice = spice;
|
||||
}
|
||||
}
|
||||
</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>
|
||||
<div ng:controller="ChildCtrl">
|
||||
<p>Good {{timeOfDay}}, {{name}}!</p>
|
||||
<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
|
||||
`timeOfDay` model
|
||||
- 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() {
|
||||
this.spices = [{"name":"pasilla", "spiciness":"mild"},
|
||||
{"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');
|
||||
});
|
||||
});
|
||||
});
|
||||
</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}
|
||||
96
docs/content/guide/dev_guide.mvc.understanding_model.ngdoc
Normal file
96
docs/content/guide/dev_guide.mvc.understanding_model.ngdoc
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@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}
|
||||
29
docs/content/guide/dev_guide.mvc.understanding_view.ngdoc
Normal file
29
docs/content/guide/dev_guide.mvc.understanding_view.ngdoc
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@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
|
||||
ng:controller} and {@link api/angular.widget.ng:view ng:view}, and through bindings of this form:
|
||||
`{{someControllerFunction()}}`. In these ways, the view can call functions in an associated
|
||||
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}
|
||||
288
docs/content/guide/dev_guide.overview.ngdoc
Normal file
288
docs/content/guide/dev_guide.overview.ngdoc
Normal file
|
|
@ -0,0 +1,288 @@
|
|||
@ngdoc overview
|
||||
@name Developer Guide: Overview
|
||||
@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
|
||||
components, along with others. Angular is far greater than the sum of its parts. It is a new,
|
||||
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>
|
||||
<br/>
|
||||
<br/>
|
||||
<table>
|
||||
<tr><td> </td><td> </td>
|
||||
<tr><td>Quantity</td><td>Cost</td></tr>
|
||||
<tr>
|
||||
<td><input name="qty" value="1"
|
||||
ng:validate="integer:0"
|
||||
ng:required/></td>
|
||||
<td><input name="cost" value="19.95"
|
||||
ng:validate="number"
|
||||
ng:required/></td>
|
||||
</tr>
|
||||
</table>
|
||||
<hr>
|
||||
<b>Total:</b> {{qty * cost | currency}}
|
||||
</doc:source>
|
||||
<!--
|
||||
<doc:scenario>
|
||||
it('should show of angular binding', function(){
|
||||
expect(binding('qty * cost')).toEqual('$19.95');
|
||||
input('qty').enter('2');
|
||||
input('cost').enter('5.00');
|
||||
expect(binding('qty * cost')).toEqual('$10.00');
|
||||
});
|
||||
</doc:scenario>
|
||||
-->
|
||||
</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.
|
||||
* Note the angular directives, {@link api/angular.widget.@ng:validate ng:validate} and {@link
|
||||
api/angular.widget.@ng:required ng:required}. You may have noticed that when you enter invalid data
|
||||
or leave the the input fields blank, the borders turn red color, and the display value disappears.
|
||||
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
|
||||
ng:bind}). The expression itself can be a combination of both an expression and a {@link
|
||||
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');
|
||||
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.
|
||||
Testing difficulty is dramatically affected by the way the code is structured.
|
||||
* It is an excellent idea to decouple the client side of an app from the server side. This allows
|
||||
development work to progress in parallel, and allows for reuse of both sides.
|
||||
* It is very helpful indeed if the framework guides developers through the entire journey of
|
||||
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,
|
||||
rather than give you functions to manipulate the DOM yourself.
|
||||
* It's not a Widget Library. There are lots of existing widget libraries that you can integrate
|
||||
with angular.
|
||||
* It's not "Just Another Templating System". A part of angular is a templating system. The
|
||||
templating subsystem of angular is different from the traditional approach for these reasons:
|
||||
* It Uses HTML/CSS syntax: This makes it easy to read and can be edited with existing HTML/CSS
|
||||
authoring tools.
|
||||
* It Extends HTML vocabulary: Angular allows you to create new HTML tags, which expand into
|
||||
dynamic UI components.
|
||||
* It Executes in the browser: Removes the round trip to the server for many operations and
|
||||
creates instant feedback for users as well as developers.
|
||||
* It Has Bidirectional data binding: The model is the single source of truth. Programmatic
|
||||
changes to the model are automatically reflected in the view. Any changes by the user to the view
|
||||
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
|
||||
application does.
|
||||
* **Manipulating HTML DOM programatically:** Manipulating HTML DOM is a cornerstone of AJAX
|
||||
applications, but it's cumbersome and error-prone. By declaratively describing how the UI should
|
||||
change as your application state changes, you are freed from low level DOM manipulation tasks. Most
|
||||
applications written with angular never have to programatically manipulate the DOM, although you
|
||||
can if you want to.
|
||||
* **Marshaling data to and from the UI:** CRUD operations make up the majority of AJAX
|
||||
applications. The flow of marshaling data from the server to an internal object to an HTML form,
|
||||
allowing users to modify the form, validating the form, displaying validation errors, returning to
|
||||
an internal model, and then back to the server, creates a lot of boilerplate code. Angular
|
||||
eliminates almost all of this boilerplate, leaving code that describes the overall flow of the
|
||||
application rather than all of the implementation details.
|
||||
* **Writing tons of initialization code just to get started:** Typically you need to write a lot of
|
||||
plumbing just to get a basic "Hello World" AJAX app working. With angular you can bootstrap your
|
||||
app easily using services, which are auto-injected into your application in a {@link
|
||||
http://code.google.com/p/google-guice/ Guice}-like dependency-injection style. This allows you to
|
||||
get started developing features quickly. As a bonus, you get full control over the initialization
|
||||
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>
|
||||
<param name="allowscriptaccess" value="always"></param>
|
||||
<embed src="http://www.youtube.com/v/elvcgVSynRg&hl=en_US&fs=1"
|
||||
type="application/x-shockwave-flash" allowscriptaccess="always"
|
||||
allowfullscreen="true" width="480" height="385"></embed>
|
||||
</object>
|
||||
|
||||
|
||||
{@link
|
||||
|
||||
https://docs.google.com/present/edit?id=0Abz6S2TvsDWSZDQ0OWdjaF8yNTRnODczazdmZg&hl=en&authkey=CO-b7oID
|
||||
|
||||
Presentation}
|
||||
|
|
||||
{@link
|
||||
|
||||
https://docs.google.com/document/edit?id=1ZHVhqC0apbzPRQcgnb1Ye-bAUbNJ-IlFMyPBPCZ2cYU&hl=en&authkey=CInnwLYO
|
||||
|
||||
Source}
|
||||
50
docs/content/guide/dev_guide.scopes.controlling_scopes.ngdoc
Normal file
50
docs/content/guide/dev_guide.scopes.controlling_scopes.ngdoc
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@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}
|
||||
51
docs/content/guide/dev_guide.scopes.ngdoc
Normal file
51
docs/content/guide/dev_guide.scopes.ngdoc
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Angular Scopes
|
||||
@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}
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@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
|
||||
objects, arrays, or primitives) is attached to angular scope properties. Angular binds the property
|
||||
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']">
|
||||
<li ng:repeat="name in names">
|
||||
Name = {{name}}!
|
||||
</li>
|
||||
</ul>
|
||||
<pre>Name={{name}}</pre>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should override the name property', function() {
|
||||
expect(using('.doc-example-live').repeater('li').row(0)).
|
||||
toEqual(['Igor']);
|
||||
expect(using('.doc-example-live').repeater('li').row(1)).
|
||||
toEqual(['Misko']);g/@
|
||||
|
||||
|
||||
expect(using('.doc-example-live').repeater('li').row(2)).
|
||||
toEqual(['Gail']);
|
||||
expect(using('.doc-example-live').repeater('li').row(3)).
|
||||
toEqual(['Kai']);
|
||||
expect(using('.doc-example-live').element('pre').text()).
|
||||
toBe('Name=Hank');
|
||||
});
|
||||
</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}
|
||||
47
docs/content/guide/dev_guide.scopes.updating_scopes.ngdoc
Normal file
47
docs/content/guide/dev_guide.scopes.updating_scopes.ngdoc
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@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
|
||||
page requires a call to `$eval()` on the root scope as `$root.$eval()`. Similarly, when a request
|
||||
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}
|
||||
65
docs/content/guide/dev_guide.scopes.working_scopes.ngdoc
Normal file
65
docs/content/guide/dev_guide.scopes.working_scopes.ngdoc
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@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'...
|
||||
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}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@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:
|
||||
- `$inject` - {Array.<string>} - Array of service ids this service depends on. These services
|
||||
will be passed as arguments into the factory function in the same order specified in the `$inject`
|
||||
array. Defaults to `[]`.
|
||||
- `$eager` - {boolean} - If true, the service factory will be called and the service will be
|
||||
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 = [];
|
||||
return function(msg) {
|
||||
msgs.push(msg);
|
||||
if (msgs.length == 3) {
|
||||
win.alert(msgs.join("\n"));
|
||||
msgs = [];
|
||||
}
|
||||
};
|
||||
}, {$inject: ['$window']});
|
||||
</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}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@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.
|
||||
The name must match the corresponding service ID registered with angular. The order of the service
|
||||
IDs matters: the order of the services in the array will be used when calling the factory function
|
||||
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() {
|
||||
// use $location service
|
||||
$loc.setHash();
|
||||
};
|
||||
this.secondMethod = function() {
|
||||
// use $log service
|
||||
$log.info('...');
|
||||
};
|
||||
}
|
||||
// which services to inject ?
|
||||
myController.$inject = ['$location', '$log'];
|
||||
</pre>
|
||||
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script type="text/javascript">
|
||||
angular.service('notify', function(win) {
|
||||
var msgs = [];
|
||||
return function(msg) {
|
||||
msgs.push(msg);
|
||||
if (msgs.length == 3) {
|
||||
win.alert(msgs.join("\n"));
|
||||
msgs = [];
|
||||
}
|
||||
};
|
||||
}, {$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" />
|
||||
<button ng:click="callNotify(message);">NOTIFY</button>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should test service', function(){
|
||||
expect(element(':input[name=message]').val()).toEqual('test');
|
||||
});
|
||||
</doc:scenario>
|
||||
</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}
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@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
|
||||
* to the console.log every 50 seconds.
|
||||
*
|
||||
* @param {*} message Message to be logged.
|
||||
*/
|
||||
angular.service('batchLog', function($defer, $log) {
|
||||
var messageQueue = [];
|
||||
|
||||
|
||||
function log() {
|
||||
if (messageQueue.length) {
|
||||
$log('batchLog messages: ', messageQueue);
|
||||
messageQueue = [];
|
||||
}
|
||||
$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.
|
||||
*/
|
||||
angular.service('routeTemplateMonitor', function($route, batchLogbatchLog) {
|
||||
$route.onChange(function() {
|
||||
batchLog($route.current ? $route.current.template : null);
|
||||
});
|
||||
}, {$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.
|
||||
* The `routeTemplateMonitor` service depends on the built-in `{@link api/angular.service.$route
|
||||
$route}` service as well as our custom `batchLog` service.
|
||||
* The `routeTemplateMonitor` service is declared to be eager, so that it is started as soon as the
|
||||
application starts.
|
||||
* To underline the need for the eager instantiation of the `routeTemplateMonitor` service, nothing
|
||||
else in the application depends on this service, and in this particular case the factory function
|
||||
of this service doesn't return anything at all.
|
||||
* Both of our services use the factory function signature as well as the `$inject` property to
|
||||
declare their dependencies. It is important that the order of the string identifiers in the array
|
||||
associated with the `$inject` property is the same as the order of argument names in the signature
|
||||
of the factory function. Unless the dependencies are inferred from the function signature, it is
|
||||
this array with IDs and their order that the injector uses to determine which services and in which
|
||||
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}
|
||||
29
docs/content/guide/dev_guide.services.ngdoc
Normal file
29
docs/content/guide/dev_guide.services.ngdoc
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@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}
|
||||
* {@link dev_guide.services.managing_dependencies Managing Service Dependencies}
|
||||
* {@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}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@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;
|
||||
//factory function body that constructs shinyNewServiceInstance
|
||||
return shinyNewServiceInstance;
|
||||
});
|
||||
</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;
|
||||
//factory function body that constructs shinyNewServiceInstance
|
||||
return shinyNewServiceInstance;
|
||||
}, {$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)
|
||||
* A guarantee is needed that the service will be instantiated at application boot time, usually
|
||||
because the service passively observes the application and it is optional for other application
|
||||
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
|
||||
given service, but that is rarely needed, except in tests where this property is crucially
|
||||
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}
|
||||
70
docs/content/guide/dev_guide.services.testing_services.ngdoc
Normal file
70
docs/content/guide/dev_guide.services.testing_services.ngdoc
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@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');
|
||||
notify('three');
|
||||
expect(mock.alert).toHaveBeenCalledWith("one\ntwo\nthree");
|
||||
});
|
||||
|
||||
|
||||
it('should clear messages after alert', function() {
|
||||
notify('one');
|
||||
notify('two');
|
||||
notify('third');
|
||||
notify('more');
|
||||
notify('two');
|
||||
notify('third');
|
||||
expect(mock.alert.callCount).toEqual(2);
|
||||
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}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@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}
|
||||
70
docs/content/guide/dev_guide.templates.css.ngdoc
Normal file
70
docs/content/guide/dev_guide.templates.css.ngdoc
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Templates: Working With CSS in Angular
|
||||
@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}
|
||||
* {@link dev_guide.templates.formatters.creating_formatters Creating Angular Formatters}
|
||||
48
docs/content/guide/dev_guide.templates.databinding.ngdoc
Normal file
48
docs/content/guide/dev_guide.templates.databinding.ngdoc
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@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
|
||||
or related sections of the view are NOT automatically reflected in the view. Worse, any changes
|
||||
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
|
||||
directives) is compiled on the browser, and second, the compilation step produces a live view. We
|
||||
say live because any changes to the view are immediately reflected in the model, and any changes in
|
||||
the model are propagated to the view. This makes the model always the single-source-of-truth for
|
||||
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}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@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">
|
||||
angular.filter('reverse', function(input, uppercase, color) {
|
||||
var out = "";
|
||||
for (var i = 0; i < input.length; i++) {
|
||||
out = input.charAt(i) + out;
|
||||
}
|
||||
// conditional based on optional argument
|
||||
if (uppercase) {
|
||||
out = out.toUpperCase();
|
||||
}
|
||||
// DOM manipulation using $element
|
||||
if (color) {
|
||||
this.$element.css('color', color);
|
||||
}
|
||||
return out;
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
<input name="text" type="text" value="hello" /><br>
|
||||
No filter: {{text}}<br>
|
||||
Reverse: {{text|reverse}}<br>
|
||||
Reverse + uppercase: {{text|reverse:true}}<br>
|
||||
Reverse + uppercase + blue: {{text|reverse:true:"blue"}}
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should reverse text', function(){
|
||||
expect(binding('text|reverse')).toEqual('olleh');
|
||||
input('text').enter('ABC');
|
||||
expect(binding('text|reverse')).toEqual('CBA');
|
||||
});
|
||||
</doc:scenario>
|
||||
</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}
|
||||
38
docs/content/guide/dev_guide.templates.filters.ngdoc
Normal file
38
docs/content/guide/dev_guide.templates.filters.ngdoc
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@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}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@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.
|
||||
* Filter with arguments: `{{1234.5678|number:5}}` => `1,234.56780`. Filters can take optional
|
||||
arguments, separated by colons in a binding. For example, the "number" filter takes a number
|
||||
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}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@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 = [];
|
||||
for (var i = 0; i < text.length; i++) {
|
||||
reversed.unshift(text.charAt(i));
|
||||
}
|
||||
return reversed.join('');
|
||||
}
|
||||
|
||||
|
||||
angular.formatter('reverse', {
|
||||
parse: function(value){
|
||||
return reverse(value||'').toUpperCase();
|
||||
},
|
||||
format: function(value){
|
||||
return reverse(value||'').toLowerCase();
|
||||
}
|
||||
});
|
||||
</pre>
|
||||
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script type="text/javascript">
|
||||
function reverse(text) {
|
||||
var reversed = [];
|
||||
for (var i = 0; i < text.length; i++) {
|
||||
reversed.unshift(text.charAt(i));
|
||||
}
|
||||
return reversed.join('');
|
||||
}
|
||||
|
||||
|
||||
angular.formatter('reverse', {
|
||||
parse: function(value){
|
||||
return reverse(value||'').toUpperCase();
|
||||
},
|
||||
format: function(value){
|
||||
return reverse(value||'').toLowerCase();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
26
docs/content/guide/dev_guide.templates.formatters.ngdoc
Normal file
26
docs/content/guide/dev_guide.templates.formatters.ngdoc
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@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}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@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>
|
||||
|
||||
75
docs/content/guide/dev_guide.templates.ngdoc
Normal file
75
docs/content/guide/dev_guide.templates.ngdoc
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@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
|
||||
is {@link api/angular.widget.@ng:repeat ng:repeat}.
|
||||
* {@link dev_guide.compiler.markup Markup} — Shorthand for a widget or a directive. The double
|
||||
curly brace notation `{{ }}` to bind expressions to elements is built-in angular markup.
|
||||
* {@link dev_guide.templates.filters Filter} — Formats your data for display to the user.
|
||||
* {@link dev_guide.templates.validators Validator} — Lets you validate user input.
|
||||
* {@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 -->
|
||||
<body ng:controller="MyController">
|
||||
<input name="foo" value="bar">
|
||||
<!-- Button tag with ng:click directive, and
|
||||
string expression 'buttonText'
|
||||
wrapped in "{{ }}" markup -->
|
||||
<button ng:click="changeFoo()">{{buttonText}}</button>
|
||||
<script src="angular.js" ng:autobind>
|
||||
</body>
|
||||
</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
|
||||
files. You "include" the partials in the main page using the {@link api/angular.service.$route
|
||||
$route} service in conjunction with the {@link api/angular.widget.ng:view ng:view} directive. An
|
||||
example of this technique is shown in the {@link tutorial/ angular tutorial}, in steps seven and
|
||||
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}
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Validators: Creating Angular Validators
|
||||
@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];
|
||||
if ( [validation succeeds] ) {
|
||||
return false;
|
||||
} else {
|
||||
return true; // No error message specified
|
||||
}
|
||||
}
|
||||
</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>
|
||||
angular.validator('upsTrackingNo', function(input, format) {
|
||||
var regexp = new RegExp("^" + format.replace(/9/g, '\\d') + "$");
|
||||
return input.match(regexp)?"":"The format must match " + format;
|
||||
});
|
||||
</script>
|
||||
<input type="text" name="trackNo" size="40"
|
||||
ng:validate="upsTrackingNo:'1Z 999 999 99 9999 999 9'"
|
||||
value="1Z 123 456 78 9012 345 6"/>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should validate correct UPS tracking number', function() {
|
||||
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')).
|
||||
toMatch(/ng-validation-error/);
|
||||
});
|
||||
</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}
|
||||
160
docs/content/guide/dev_guide.templates.validators.ngdoc
Normal file
160
docs/content/guide/dev_guide.templates.validators.ngdoc
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@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}
|
||||
* {@link api/angular.validator.date date}
|
||||
* {@link api/angular.validator.email email address}
|
||||
* {@link api/angular.validator.json JSON}
|
||||
* {@link api/angular.validator.regexp regular expressions}
|
||||
* {@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">
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should validate the default number string', function() {
|
||||
expect(element('input[name=number]').attr('class')).
|
||||
not().toMatch(/ng-validation-error/);
|
||||
});
|
||||
it('should not validate "foo"', function() {
|
||||
input('number').enter('foo');
|
||||
expect(element('input[name=number]').attr('class')).
|
||||
toMatch(/ng-validation-error/);
|
||||
});
|
||||
</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];
|
||||
if ( [validation succeeds] ) {
|
||||
return false;
|
||||
} else {
|
||||
return true; // No error message specified
|
||||
}
|
||||
}
|
||||
</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>
|
||||
angular.validator('upsTrackingNo', function(input, format) {
|
||||
var regexp = new RegExp("^" + format.replace(/9/g, '\\d') + "$");
|
||||
return input.match(regexp)?"":"The format must match " + format;
|
||||
});
|
||||
</script>
|
||||
<input type="text" name="trackNo" size="40"
|
||||
ng:validate="upsTrackingNo:'1Z 999 999 99 9999 999 9'"
|
||||
value="1Z 123 456 78 9012 345 6"/>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should validate correct UPS tracking number', function() {
|
||||
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')).
|
||||
toMatch(/ng-validation-error/);
|
||||
});
|
||||
</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}
|
||||
323
docs/content/guide/dev_guide.unit-testing.ngdoc
Normal file
323
docs/content/guide/dev_guide.unit-testing.ngdoc
Normal file
|
|
@ -0,0 +1,323 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@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
|
||||
angular which makes testing your angular applications easy. So there is no excuse for not do it.
|
||||
# It is all about NOT mixing concerns
|
||||
Unit testing as the name implies is about testing individual units of code. Unit tests try to
|
||||
answer the question: Did I think about the logic correctly. Does the sort function order the list
|
||||
in the right order. In order to answer such question it is very important that we can isolate it.
|
||||
That is because when we are testing the sort function we don't want to be forced into crating
|
||||
related pieces such as the DOM elements, or making any XHR calls in getting the data to sort. While
|
||||
this may seem obvious it usually is very difficult to be able to call an individual function on a
|
||||
typical project. The reason is that the developers often time mix concerns, and they end up with a
|
||||
piece of code which does everything. It reads the data from XHR, it sorts it and then it
|
||||
manipulates the DOM. With angular we try to make it easy for you to do the right thing, and so we
|
||||
provide dependency injection for your XHR (which you can mock out) and we crated abstraction which
|
||||
allow you to sort your model without having to resort to manipulating the DOM. So that in the end,
|
||||
it is easy to write a sort function which sorts some data, so that your test can create a data set,
|
||||
apply the function, and assert that the resulting model is in the correct order. The test does not
|
||||
have to wait for XHR, or create the right kind of DOM, or assert that your function has mutated the
|
||||
DOM in the right way. Angular is written with testability in mind, but it still requires that you
|
||||
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.
|
||||
2. You could look for it in a well know place, also known as global singleton.
|
||||
3. You could ask a registry (also known as service registry) for it. (But how do you get a hold of
|
||||
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(){
|
||||
var xhr = new XHR();
|
||||
xhr.open(method, url, true);
|
||||
xhr.onreadystatechange = function(){...}
|
||||
xhr.send();
|
||||
}
|
||||
}
|
||||
</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;
|
||||
XHR = function MockXHR(){};
|
||||
var myClass = new MyClass();
|
||||
myClass.doWork();
|
||||
// assert that MockXHR got called with the right arguments
|
||||
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(){
|
||||
global.xhr({
|
||||
method:'...',
|
||||
url:'...',
|
||||
complete:function(response){ ... }
|
||||
})
|
||||
}
|
||||
}
|
||||
</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
|
||||
order to replace it with call to a mock method. For further explanation why this is bad see: {@link
|
||||
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;
|
||||
glabal.xhr = function mockXHR(){};
|
||||
var myClass = new MyClass();
|
||||
myClass.doWork();
|
||||
// assert that mockXHR got called with the right arguments
|
||||
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 = ????;
|
||||
this.doWork = function(){
|
||||
var xhr = serviceRegistry.get('xhr');
|
||||
xhr({
|
||||
method:'...',
|
||||
url:'...',
|
||||
complete:function(response){ ... }
|
||||
})
|
||||
}
|
||||
</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;
|
||||
glabal.serviceLocator.set('xhr', function mockXHR(){});
|
||||
var myClass = new MyClass();
|
||||
myClass.doWork();
|
||||
// assert that mockXHR got called with the right arguments
|
||||
glabal.serviceLocator = oldServiceLocator; // if you forget this bad things will happen
|
||||
</pre>
|
||||
|
||||
|
||||
|
||||
|
||||
### Passing in Dependencies:
|
||||
Lastly the dependency can be passed in.
|
||||
|
||||
|
||||
<pre>
|
||||
function MyClass(xhr) {
|
||||
this.doWork = function(){
|
||||
xhr({
|
||||
method:'...',
|
||||
url:'...',
|
||||
complete:function(response){ ... }
|
||||
})
|
||||
}
|
||||
</pre>
|
||||
|
||||
|
||||
This is the proferred way since the code makes no assumptions as to where the `xhr` comes from,
|
||||
rather that who-ever crated the class was responsible for passing it in. Since the creator of the
|
||||
class should be different code the the user of the class, it separates the responsibility of
|
||||
creation from the logic, and that is what dependency-injection is in a nutshell.
|
||||
|
||||
|
||||
The class above is very testable, since in the test we can write:
|
||||
<pre>
|
||||
function xhrMock(args) {...}
|
||||
var myClass = new MyClass(xhrMock);
|
||||
myClass.doWork();
|
||||
// assert that xhrMock got called with the right arguments
|
||||
</pre>
|
||||
|
||||
|
||||
Notice that no global variables were harmed in the writing of this test.
|
||||
|
||||
|
||||
Angular comes with {@link dev_guide.di dependency-injection} built in which makes the right thin
|
||||
the easy thing to do, but you still need to do it if you wish to take advantage of the testability
|
||||
story.
|
||||
|
||||
|
||||
## Controllers
|
||||
What makes each application unique is its logic, which is what we would like to test. If the logic
|
||||
for your application is mixed in with DOM manipulation, it will be hard to test as in the example
|
||||
below:
|
||||
|
||||
|
||||
<pre>
|
||||
function PasswordController(){
|
||||
// get references to DOM elements
|
||||
var msg = $('.ex1 span');
|
||||
var input = $('.ex1 input');
|
||||
var strength;
|
||||
|
||||
this.grade = function(){
|
||||
msg.removeClass(strength);
|
||||
var pwd = input.val();
|
||||
password.text(pwd);
|
||||
if (pwd.length > 8) {
|
||||
strength = 'strong';
|
||||
} else if (pwd.length > 3) {
|
||||
strength = 'medium';
|
||||
} else {
|
||||
strength = 'weak';
|
||||
}
|
||||
msg
|
||||
.addClass(strength)
|
||||
.text(strength);
|
||||
}
|
||||
}
|
||||
</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>');
|
||||
$('body').html('<div class="ex1">')
|
||||
.find('div')
|
||||
.append(input)
|
||||
.append(span);
|
||||
var pc = new PasswordController();
|
||||
input.val('abc');
|
||||
pc.grade();
|
||||
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 = '';
|
||||
this.grade = function(){
|
||||
var size = this.password.length;
|
||||
if (size > 8) {
|
||||
this.strength = 'strong';
|
||||
} else if (size > 3) {
|
||||
this.strength = 'medium';
|
||||
} else {
|
||||
this.strength = 'weak';
|
||||
}
|
||||
};
|
||||
}
|
||||
</pre>
|
||||
|
||||
|
||||
and the tests is straight forward
|
||||
|
||||
|
||||
<pre>
|
||||
var pc = new PasswordController();
|
||||
pc.password('abc');
|
||||
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
|
||||
oue
|
||||
# Preferred way of Testing
|
||||
uo
|
||||
## JavaScriptTestDriver
|
||||
ou
|
||||
## Jasmine
|
||||
ou
|
||||
## Sample project
|
||||
uoe
|
||||
|
|
@ -3,32 +3,78 @@
|
|||
@name Developer Guide
|
||||
@description
|
||||
|
||||
* {@link guide.overview Overview} - An overview of angular, including its philosophy and how it
|
||||
works.
|
||||
* {@link guide.bootstrap Bootstrap} - How to bootstrap your application to the angular environment.
|
||||
* {@link guide.template Template} - How to define your application's view using HTML, CSS, and
|
||||
other built-in angular constructs.
|
||||
* {@link guide.compiler Compiler} - All about the HTML compiler that's at the core of angular.
|
||||
* {@link angular.directive Directive} - How to use XML attributes to augment an existing DOM
|
||||
element.
|
||||
* {@link angular.markup Markup} - How to use markup to create shorthand for a widget or a
|
||||
directive. For example, markup is what allows you to use the double curly brace notation
|
||||
`{{}}` to bind expressions to elements.
|
||||
* {@link guide.data-binding Data Binding} - About the mechanism that keeps the model the single
|
||||
source of truth of your application at all times, with the view as a live projection of the
|
||||
model.
|
||||
* {@link angular.filter Filter} - How to format your data for display to the user.
|
||||
* {@link angular.widget Widget} - How to create new DOM elements that the browser doesn't already
|
||||
understand.
|
||||
* {@link angular.validator Validator} - How to validate user input.
|
||||
* {@link angular.formatter Formatter} - How to format stored data to user-readable text and
|
||||
parse the text back to the stored form.
|
||||
* {@link guide.css CSS} - Built-in CSS classes, when angular assigns them, and how to override
|
||||
their styles.
|
||||
* {@link angular.scope Scope} - The model in the model-view-controller design pattern. You can
|
||||
think about scopes as the JavaScript objects that have extra APIs for registering watchers.
|
||||
* {@link guide.expression Expression} - The bindings that are embedded in an angular View.
|
||||
* {@link angular.service Service} - Objects that are wired through dependency injection and then
|
||||
injected into the root scope.
|
||||
* {@link guide.testing Testing}
|
||||
* service:$browser(mock)
|
||||
|
||||
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 intro/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}
|
||||
|
|
|
|||
|
|
@ -2,14 +2,10 @@
|
|||
@name Contributing
|
||||
@description
|
||||
|
||||
<a name="H1_1"></a>
|
||||
# Open Source
|
||||
|
||||
`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 href="#H1_1">License</a>
|
||||
* <a href="#H1_2">Contributing to Source Code</a>
|
||||
* <a href="#H1_3">Applying Code Standards</a>
|
||||
* <a href="#H1_4">Checking Out and Building `Angular`</a>
|
||||
|
|
@ -17,217 +13,348 @@ this page.
|
|||
|
||||
|
||||
|
||||
|
||||
<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-x.y.z-<git sha>.tgz` — This is the complete tarball, which contains all of the release
|
||||
build artifacts.
|
||||
|
||||
* `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
|
||||
|
||||
./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:
|
||||
* To delete the branch in Github, run:
|
||||
|
||||
1. To delete the branch in Github, run:
|
||||
|
||||
|
||||
git push origin :my-fix-branch
|
||||
|
||||
* To check out the master branch, run:
|
||||
|
||||
2. To check out the master branch, run:
|
||||
|
||||
|
||||
git checkout master
|
||||
|
||||
* To delete a local branch, run:
|
||||
|
||||
3. To delete a local branch, run:
|
||||
|
||||
|
||||
git branch -D my-fix-branch
|
||||
|
||||
* To update your master with the latest upstream version, run:
|
||||
|
||||
4. To update your master with the latest upstream version, run:
|
||||
|
||||
|
||||
git pull --ff upstream master
|
||||
|
||||
|
||||
That's it! Thank you for your contribution!
|
||||
|
|
|
|||
|
|
@ -3,24 +3,30 @@
|
|||
@name Downloading
|
||||
@description
|
||||
|
||||
# Including angular scripts from code.angularjs.org
|
||||
|
||||
To get started quickly you without worrying about downloading anything and maintaining a local copy,
|
||||
you can point your html `script` tag directly to <http://code.angularjs.org/> urls.
|
||||
# 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 kinds of urls you care about:
|
||||
|
||||
* http://code.angularjs.org/angular-<version>.js
|
||||
* http://code.angularjs.org/angular-<version>.min.js
|
||||
There are two types of angular script URLs you can point to, one for development and one for
|
||||
production:
|
||||
|
||||
The first one is non-minified version, suitable for web development. The latter one is minified
|
||||
version, which we strongly suggest you use in production.
|
||||
|
||||
To point your code to let's say angular version 0.9.12, use the following template:
|
||||
* __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>
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
<head>
|
||||
<title>My Angular App</title>
|
||||
<script src="http://code.angularjs.org/angular-0.9.12.js" ng:autobind></script>
|
||||
|
|
@ -31,40 +37,51 @@ To point your code to let's say angular version 0.9.12, use the following templa
|
|||
</pre>
|
||||
|
||||
|
||||
# Downloading and hosting the files on your own
|
||||
|
||||
This options is for those who want to work with angular offline, or 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 angular
|
||||
versions since we started releasing versioned build artifacts (quite late in the project lifetime).
|
||||
# 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.
|
||||
|
||||
Each directory contains all artifacts that we released for a particular version. Once you navigate
|
||||
to one of these directories you'll see the following list 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.
|
||||
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.
|
||||
|
||||
* `angular-<version>.min.js` - This is a minified and obfuscated version of
|
||||
`angular-<version>.js` created with 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 which contains all the other files released
|
||||
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 resides under the same parent path as
|
||||
`angular-<version>.js` or `angular-<version>.min.js`.
|
||||
|
||||
* `angular-mocks-<version>.js` - This file contains implementation of mocks that we provide to you
|
||||
to make testing angular apps even easier. Your unit/integration test harness should load this file
|
||||
after `angular-<version>.js` is loaded.
|
||||
* __`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-scenario-<version>.js` - This file is a very nifty JavaScript file, which allows you to
|
||||
write and execute end to end tests for angular applications.
|
||||
|
||||
* `docs-<version>` - this directory contains all the files that compose the
|
||||
* __`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!
|
||||
our docs, or even more importantly, view the docs offline.
|
||||
|
|
|
|||
|
|
@ -3,36 +3,49 @@
|
|||
@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 intro.downloading downloading}.
|
||||
|
||||
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
|
||||
|
|
@ -40,42 +53,59 @@ 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 angular.element jQuery lite}. If jQuery is present in the path, angular uses it to
|
||||
{@link api/angular.element jQuery lite}. If jQuery is present in the path, angular uses it to
|
||||
manipulate the DOM.
|
||||
|
||||
|
||||
### What is testability like in angular?
|
||||
|
||||
|
||||
Very testable. It has an integrated dependency injection framework. See
|
||||
{@link angular.service service} for details.
|
||||
{@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}".
|
||||
"{@link http://www.youtube.com/watch?v=elvcgVSynRg| Angular: A Radically Different Way of Building
|
||||
AJAX Apps}".
|
||||
|
||||
|
||||
### How is angular licensed?
|
||||
|
||||
|
||||
The MIT License.
|
||||
|
|
|
|||
|
|
@ -3,68 +3,89 @@
|
|||
@name Getting Started
|
||||
@description
|
||||
|
||||
|
||||
# Hello World!
|
||||
|
||||
A great way for you to get started with `angular` is to create the tradtional
|
||||
|
||||
A great way for you to get started with `angular` is to create the tradtional
|
||||
"Hello World!" app:
|
||||
|
||||
1. In your favorite text editor, create an HTML file
|
||||
(for example, `helloworld.html`).
|
||||
|
||||
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
|
||||
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 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>
|
||||
<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.bootstrap Bootstrap}.)
|
||||
|
||||
Finally, this line in the `<body>` of the page is the template that describes
|
||||
(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
|
||||
|
||||
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
|
||||
|
||||
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:
|
||||
|
||||
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"/>
|
||||
|
|
@ -73,74 +94,100 @@ 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 angular.widget widget} called `yourname` is bound to a model variable called
|
||||
|
||||
* 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 variable `yourname` to the
|
||||
* The double curly braces notation binds the variable `yourname` to the
|
||||
greeting text.
|
||||
<!--
|
||||
* The variable `yourname` is implicitly created in the root scope.
|
||||
-->
|
||||
* You did not need to explicitly register an event listener or define an event
|
||||
* 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.data-binding bi-directional data binding}. Any changes to the input field are immediately
|
||||
reflected in the model (one direction), and any changes to the model are
|
||||
|
||||
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
|
||||
field are immediately
|
||||
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:
|
||||
|
||||
# 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!
|
||||
|
||||
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.
|
||||
|
||||
## Scope
|
||||
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.
|
||||
|
||||
The Model consists of one or more JavaScript objects, arrays, or primitive types.
|
||||
These are referenced from the scope. There are no restrictions on what the Model
|
||||
can be or what structure it should have. The only requirement is that it is
|
||||
referenced by the scope.
|
||||
|
||||
The following illustration shows the parts of an `angular` application and how they
|
||||
work together:
|
||||
|
||||
|
||||
## 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:
|
||||
|
||||
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.
|
||||
* Services include Dependency-Injection, XHR, caching, URL routing, and browser abstraction.
|
||||
|
||||
|
||||
|
||||
|
||||
# Where To Go Next
|
||||
|
||||
* 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}.
|
||||
|
||||
* For explanations of the `angular` concepts presented in the examples on this
|
||||
page, see the {@link guide Developer Guide}.
|
||||
* 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}.
|
||||
|
|
|
|||
|
|
@ -38,14 +38,14 @@ When you finish the tutorial you will be able to:
|
|||
* Identify resources for learning more about angular
|
||||
|
||||
|
||||
Mac and Linux users can work through the tutorial, run tests, and experiment with the code using
|
||||
Git or the snapshots described below. Windows users will be able read the tutorial but won't be
|
||||
able to run the tests or experiment with the code.
|
||||
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 {@link
|
||||
http://docs.angularjs.org/#!started started}.
|
||||
really digging into it. If you're looking for a shorter introduction to angular, check out the
|
||||
{@link misc/started Getting Started} document.
|
||||
|
||||
|
||||
|
||||
|
|
@ -54,21 +54,8 @@ http://docs.angularjs.org/#!started started}.
|
|||
|
||||
|
||||
|
||||
<a name="PreReqs"></a>
|
||||
# Prerequisites
|
||||
|
||||
|
||||
To run the tutorial app and tests on your machine you will need the following:
|
||||
|
||||
|
||||
* A Mac or Linux machine (required for running the tutorial scripts)
|
||||
* An http server running on your system. Mac and Linux machines typically have Apache preinstalled.
|
||||
If you don't already have an http server installed, you can install `node.js` ({@link
|
||||
https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager node.js install guide})
|
||||
or another http sever.
|
||||
* {@link http://java.com Java}.
|
||||
* A web browser.
|
||||
* A text editor.
|
||||
|
||||
|
||||
|
||||
|
|
@ -76,59 +63,113 @@ or another http sever.
|
|||
# Working with the code
|
||||
|
||||
|
||||
There are two ways that you can you follow this tutorial and hack on 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:
|
||||
|
||||
|
||||
## Using Git
|
||||
<doc:tutorial-instructions show="true">
|
||||
<doc:tutorial-instruction id="git-mac" title="Git on Mac/Linux">
|
||||
<ol>
|
||||
<li><p>Verify that you have <a href="http://java.com/">Java</a> installed by running the
|
||||
following command in a terminal window:</p>
|
||||
<pre><code>java -version</code></pre>
|
||||
<p>You will need Java to run unit tests.</p></li>
|
||||
<li><p>Get Git from <a href="http://git-scm.com/download">here</a></p>
|
||||
<p>You can build it from source or use pre-compiled package.</p></li>
|
||||
<li><p>Clone the angular-phonecat repository located at <a
|
||||
href="https://github.com/angular/angular-phonecat">Github</a> by running this command:</p>
|
||||
<pre><code>git clone git://github.com/angular/angular-phonecat.git</code></pre>
|
||||
<p>This will create <code>angular-phonecat</code> directory in current directory.</p></li>
|
||||
<li><p>Change your current directory to <code>angular-phonecat</code>:</p>
|
||||
<pre><code>cd angular-phonecat</code></pre>
|
||||
<p>The tutorial instructions assume you are running all commands from this directory.</p></li>
|
||||
<li><p>You'll also need an http server running on your system. Mac and Linux machines
|
||||
typically have Apache preinstalled.</p>
|
||||
<p>If you don't already have an http server installed, you can <a
|
||||
href="https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager">install
|
||||
node.js</a> and use it to run <code>scripts/web-server.js</code> — a simple bundled http
|
||||
server.</p></li>
|
||||
</ol>
|
||||
</doc:tutorial-instruction>
|
||||
|
||||
|
||||
The following instructions are for Git users. If you're not a Git user, skip down to the "Using
|
||||
Snapshots" section.
|
||||
<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
|
||||
<code>java</code> executable is on your <code>PATH</code> by running this command in windows
|
||||
command line:</p>
|
||||
<pre><code>java -version</code></pre>
|
||||
<p>You will need Java to run unit tests.</p></li>
|
||||
<li><p>Install msysGit from <a href="http://git-scm.com/download">here</a></p></li>
|
||||
<li><p>Open msysGit bash and clone the angular-phonecat repository located at <a
|
||||
href="https://github.com/angular/angular-phonecat">Github</a> by running this command:</p>
|
||||
<pre><code>git clone git://github.com/angular/angular-phonecat.git</code></pre>
|
||||
<p>This will create angular-phonecat directory in your current directory.</p></li>
|
||||
<li><p>Change your current directory to angular-phonecat:</p>
|
||||
<pre><code>cd angular-phonecat</code></pre>
|
||||
<p>The tutorial instructions assume you are running all commands from this directory.</p>
|
||||
<p>You should run all <code>git</code> commands from msysGit bash.</p>
|
||||
<p>Other commands like <code>test-server.bat</code> or <code>test.bat</code> that will be
|
||||
introduced soon, should be executed from the windows command line.</li>
|
||||
<li><p>You'll also need an http server running on your system.</p>
|
||||
<p>If you don't already have an http server installed, you can install <a
|
||||
href="http://nodejs.org/">node.js</a>. Just download <a
|
||||
href="http://node-js.prcn.co.cc/">pre-compiled binaries</a>, unzip them and add
|
||||
<code>nodejs\bin</code> into your <code>PATH</code> and use <code>node</code> to run
|
||||
<code>scripts\web-server.js</code> — a simple bundled http server.</p></li>
|
||||
</ol>
|
||||
</doc:tutorial-instruction>
|
||||
|
||||
|
||||
1. Check to be sure you have all of the <a href="#PreReqs">prerequisites</a> on your system.
|
||||
<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
|
||||
following command in a terminal window:</p>
|
||||
<pre><code>java -version</code></pre>
|
||||
<p>You will need Java to run unit tests.</p></li>
|
||||
<li><p>Download the <a href="http://code.angularjs.org/angular-phonecat/">zip archive</a>
|
||||
with all files and unzip them into [tutorial-dir] directory</p></li>
|
||||
<li><p>Change your current directory to [tutorial-dir]/sanbox:</p>
|
||||
<pre><code>cd [tutorial-dir]/sandbox</code></pre>
|
||||
<p>The tutorial instructions assume you are running all commands from this directory.</p></li>
|
||||
<li><p>You'll also need an http server running on your system. Mac and Linux machines
|
||||
typically have Apache preinstalled.</p>
|
||||
<p>If you don't already have an http server installed, you can <a
|
||||
href="https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager">install
|
||||
node.js</a> and use it to run <code>scripts/web-server.js</code> — a simple bundled http
|
||||
server.</p></li>
|
||||
</ol>
|
||||
</doc:tutorial-instruction>
|
||||
|
||||
|
||||
2. Clone the angular-phonecat repository located at {@link
|
||||
https://github.com/angular/angular-phonecat GitHub} by running the following command in a terminal:
|
||||
<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
|
||||
<code>java</code> executable is on your <code>PATH</code> by running this command in windows
|
||||
command line:</p>
|
||||
<pre><code>java -version</code></pre>
|
||||
<p>You will need Java to run unit tests.</p></li>
|
||||
<li><p>Download the <a href="http://code.angularjs.org/angular-phonecat/">zip archive</a>
|
||||
with all files and unzip them into [tutorial-dir] directory</p></li>
|
||||
<li><p>Change your current directory to [tutorial-dir]/sanbox:</p>
|
||||
<pre><code>cd [tutorial-dir]/sandbox</code></pre>
|
||||
<p>The tutorial instructions assume you are running all commands from this directory.</p></li>
|
||||
<li><p>You'll also need an http server running on your system.</p>
|
||||
<p>If you don't already have an http server installed, you can install <a
|
||||
href="http://nodejs.org/">node.js</a>. Just download <a
|
||||
href="http://node-js.prcn.co.cc/">pre-compiled binaries</a>, unzip them and add
|
||||
<code>nodejs\bin</code> into your <code>PATH</code> and use <code>node</code> to run
|
||||
<code>scripts\web-server.js</code> — a simple bundled http server.</p></li>
|
||||
</ol>
|
||||
</doc:tutorial-instruction>
|
||||
</doc:tutorial-instructions>
|
||||
|
||||
|
||||
git clone git://github.com/angular/angular-phonecat.git
|
||||
For either work flow you'll also need a web browser and your favorite text editor.
|
||||
|
||||
|
||||
This will create a directory called `angular-phonecat` in the current directory.
|
||||
|
||||
|
||||
3. Change your current directory to `angular-phonecat`.
|
||||
|
||||
|
||||
cd angular-phonecat
|
||||
|
||||
|
||||
The tutorial instructions assume you are running all commands from this directory.
|
||||
|
||||
|
||||
|
||||
|
||||
## Using Snapshots
|
||||
|
||||
|
||||
1. Check to be sure you have all of the <a href="#PreReqs">prerequisites</a> on your system.
|
||||
|
||||
|
||||
2. {@link http://code.angularjs.org/angular-phonecat-snapshots.zip Download the zip archive} with
|
||||
all files and unzip them into `[tutorial-dir]` directory.
|
||||
|
||||
|
||||
|
||||
|
||||
3. Change directories to `[tutorial-dir]/sandbox`.
|
||||
|
||||
|
||||
cd [tutorial-dir]/sandbox
|
||||
|
||||
|
||||
|
||||
|
||||
Let's get going with {@link tutorial/step_00 step 0}.
|
||||
Let's get going with {@link step_00 step 0}.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,17 +1,11 @@
|
|||
@ngdoc overview
|
||||
@name Tutorial: Step 0
|
||||
@name Tutorial: 0 - angular-seed
|
||||
@description
|
||||
|
||||
|
||||
<table id="tutorial_nav">
|
||||
<tr>
|
||||
<td id="previous_step">{@link tutorial Previous}</td>
|
||||
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-0/app Live Demo}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">Code Diff</td>
|
||||
<td id="next_step">{@link tutorial.step_01 Next}</td>
|
||||
</tr>
|
||||
</table>
|
||||
<ul doc:tutorial-nav="0"></ul>
|
||||
|
||||
|
||||
|
||||
|
||||
You are now ready to build the phone cat application. In this step, you will become familiar with
|
||||
|
|
@ -19,40 +13,146 @@ the most important source code files, learn how to start the development servers
|
|||
angular-seed, and run the application in the browser.
|
||||
|
||||
|
||||
1. Do one of the following:
|
||||
|
||||
|
||||
* Git users: In the `angular-phonecat` directory, run this command:
|
||||
<doc:tutorial-instructions show="true">
|
||||
<doc:tutorial-instruction id="git-mac" title="Git on Mac/Linux">
|
||||
<ol>
|
||||
<li><p>In angular-phonecat directory, run this command:</p>
|
||||
<pre><code>git checkout -f step-0</code></pre>
|
||||
<p>This resets your workspace to Step 0 of the tutorial app.</p>
|
||||
<p>You must repeat this for every future step in the tutorial and change the number to
|
||||
the number of the step you are on. This will cause any changes you made within
|
||||
your working directory to be lost.</p></li>
|
||||
|
||||
|
||||
git checkout -f step-0
|
||||
<li>To see the app running in a browser, do one of the following:
|
||||
<ul>
|
||||
<li><b>For node.js users:</b>
|
||||
<ol>
|
||||
<li>In a <i>separate</i> terminal tab or window, run
|
||||
<code>./scripts/web-server.js</code> to start the web server.</li>
|
||||
<li>Open a browser window for the app and navigate to <a
|
||||
href="http://localhost:8000/app/index.html">http://localhost:8000/app/index.html</a></li>
|
||||
</ol>
|
||||
</li>
|
||||
<li><b>For other http servers:</b>
|
||||
<ol>
|
||||
<li>Configure the server to serve the files in the <code>angular-phonecat</code>
|
||||
directory.</li>
|
||||
<li>Navigate in your browser to
|
||||
<code>http://localhost:[port-number]/[context-path]/app/index.html</code>.</li>
|
||||
</ol>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ol>
|
||||
</doc:tutorial-instruction>
|
||||
|
||||
|
||||
* Snapshot users: In the `[tutorial-dir]/sandbox` directory, run this command:
|
||||
|
||||
|
||||
./goto_step.sh 0
|
||||
<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>
|
||||
<pre><code>git checkout -f step-0</code></pre>
|
||||
<p>This resets your workspace to Step 0 of the tutorial app.</p>
|
||||
<p>You must repeat this for every future step in the tutorial and change the number to
|
||||
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>
|
||||
<ol>
|
||||
<li>In a <i>separate</i> terminal tab or window, run <code>node
|
||||
scripts\web-server.js</code> to start the web server.</li>
|
||||
<li>Open a browser window for the app and navigate to <a
|
||||
href="http://localhost:8000/app/index.html">http://localhost:8000/app/index.html</a></li>
|
||||
</ol>
|
||||
</li>
|
||||
<li><b>For other http servers:</b>
|
||||
<ol>
|
||||
<li>Configure the server to serve the files in the <code>angular-phonecat</code>
|
||||
directory.</li>
|
||||
<li>Navigate in your browser to
|
||||
<code>http://localhost:[port-number]/[context-path]/app/index.html</code>.</li>
|
||||
</ol>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ol>
|
||||
</doc:tutorial-instruction>
|
||||
|
||||
|
||||
This resets your workspace to Step 0 of the tutorial app.
|
||||
|
||||
|
||||
You must repeat this for every future step in the tutorial and change the number to the number of
|
||||
the step you are on. Either command will cause any changes you made within your working directory
|
||||
to be lost.
|
||||
<doc:tutorial-instruction id="ss-mac" title="Snapshots on Mac/Linux">
|
||||
<ol>
|
||||
<li><p>In angular-phonecat directory, run this command:</p>
|
||||
<pre><code>./goto_step.sh 0</code></pre>
|
||||
<p>This resets your workspace to Step 0 of the tutorial app.</p>
|
||||
<p>You must repeat this for every future step in the tutorial and change the number to
|
||||
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>
|
||||
<ol>
|
||||
<li>In a <i>separate</i> terminal tab or window, run
|
||||
<code>./scripts/web-server.js</code> to start the web server.</li>
|
||||
<li>Open a browser window for the app and navigate to <a
|
||||
href="http://localhost:8000/app/index.html">http://localhost:8000/app/index.html</a></li>
|
||||
</ol>
|
||||
</li>
|
||||
<li><b>For other http servers:</b>
|
||||
<ol>
|
||||
<li>Configure the server to serve the files in the angular-phonecat
|
||||
<code>sandbox</code> directory.</li>
|
||||
<li>Navigate in your browser to
|
||||
<code>http://localhost:[port-number]/[context-path]/app/index.html</code>.</li>
|
||||
</ol>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ol>
|
||||
</doc:tutorial-instruction>
|
||||
|
||||
|
||||
2. To see the app running in a browser, do one of the following:
|
||||
* __For node.js users:__
|
||||
1. In a _separate_ terminal tab or window, run `./scripts/web-server.js` to start the app
|
||||
server.
|
||||
2. Open a browser window for the app and navigate to http://localhost:8000/app/index.html.
|
||||
|
||||
|
||||
* __For other http servers:__
|
||||
1. Configure the server to serve the files in the `angular-phonecat` directory.
|
||||
2. Navigate in your browser to
|
||||
http://localhost:[*port-number*]/[*context-path*]/app/index.html.
|
||||
<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>
|
||||
<pre><code>goto_step.bat 0</code></pre>
|
||||
<p>This resets your workspace to Step 0 of the tutorial app.</p>
|
||||
<p>You must repeat this for every future step in the tutorial and change the number to
|
||||
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>
|
||||
<ol>
|
||||
<li>In a <i>separate</i> terminal tab or window, run <code>node
|
||||
scripts\web-server.js</code> to start the web server.</li>
|
||||
<li>Open a browser window for the app and navigate to <a
|
||||
href="http://localhost:8000/app/index.html">http://localhost:8000/app/index.html</a></li>
|
||||
</ol>
|
||||
</li>
|
||||
<li><b>For other http servers:</b>
|
||||
<ol>
|
||||
<li>Configure the server to serve the files in the angular-phonecat
|
||||
<code>sandbox</code> directory.</li>
|
||||
<li>Navigate in your browser to
|
||||
<code>http://localhost:[port-number]/[context-path]/app/index.html</code>.</li>
|
||||
</ol>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ol>
|
||||
</doc:tutorial-instruction>
|
||||
</doc:tutorial-instructions>
|
||||
|
||||
|
||||
|
||||
|
||||
You can now see the page in your browser. It's not very exciting, but that's OK.
|
||||
|
|
@ -93,7 +193,7 @@ __`app/index.html`:__
|
|||
* xmlns declaration
|
||||
|
||||
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
|
||||
|
||||
This `xmlns` declaration for the `ng` namespace must be specified in all angular applications in
|
||||
|
|
@ -104,7 +204,7 @@ using XHTML or HTML).
|
|||
* angular script tag
|
||||
|
||||
|
||||
<script src="lib/angular/angular.js" ng:autobind>
|
||||
<script src="lib/angular/angular.js" ng:autobind>
|
||||
|
||||
|
||||
This single line of code is all that is needed to bootstrap an angular application.
|
||||
|
|
@ -112,12 +212,12 @@ using XHTML or HTML).
|
|||
|
||||
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 angular.directive.ng:autobind ng:autobind} attribute. If angular finds
|
||||
looks for the {@link api/angular.directive.ng:autobind ng:autobind} attribute. If angular finds
|
||||
`ng:autobind`, it creates a root scope for the application and associates it with the `<html>`
|
||||
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
|
||||
|
|
@ -151,12 +251,6 @@ For the purposes of this tutorial, we modified the angular-seed with the followi
|
|||
Now let's go to step 1 and add some content to the web app.
|
||||
|
||||
|
||||
<table id="tutorial_nav">
|
||||
<tr>
|
||||
<td id="previous_step">{@link tutorial Previous}</td>
|
||||
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-0/app Live Demo}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">Code Diff</td>
|
||||
<td id="next_step">{@link tutorial.step_01 Next}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<ul doc:tutorial-nav="0"></ul>
|
||||
|
|
|
|||
|
|
@ -1,17 +1,11 @@
|
|||
@ngdoc overview
|
||||
@name Tutorial: Step 1
|
||||
@name Tutorial: 1 - Static Template
|
||||
@description
|
||||
<table id="tutorial_nav">
|
||||
<tr>
|
||||
<td id="previous_step">{@link tutorial.step_00 Previous}</td>
|
||||
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-1/app Live
|
||||
Demo}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">
|
||||
{@link https://github.com/angular/angular-phonecat/compare/step-0...step-1 Code Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_02 Next}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<ul doc:tutorial-nav="1"></ul>
|
||||
|
||||
|
||||
|
||||
|
||||
In order to illustrate how angular enhances standard HTML, you will create a purely *static* HTML
|
||||
|
|
@ -22,23 +16,11 @@ 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.
|
||||
|
||||
|
||||
1. Reset the workspace to step 1.
|
||||
|
||||
|
||||
* Git users run:
|
||||
<doc:tutorial-instructions step="1" show="true"></doc:tutorial-instructions>
|
||||
|
||||
|
||||
git checkout -f step-1
|
||||
|
||||
|
||||
* Snapshot users run:
|
||||
|
||||
|
||||
./goto_step.sh 1
|
||||
|
||||
|
||||
2. Refresh your browser or check the app out on {@link
|
||||
http://angular.github.com/angular-phonecat/step-1/app anglar's server}.
|
||||
|
||||
|
||||
The page now contains a list with information about two phones.
|
||||
|
|
@ -77,9 +59,7 @@ __`app/index.html`:__
|
|||
* Try adding more static HTML to `index.html`. For example:
|
||||
|
||||
|
||||
<pre>
|
||||
<p>Total number of phones: 2</p>
|
||||
</pre>
|
||||
<p>Total number of phones: 2</p>
|
||||
|
||||
|
||||
|
||||
|
|
@ -91,15 +71,7 @@ This addition to your app uses static HTML to display the list. Now, let's go to
|
|||
how to use angular to dynamically generate the same list.
|
||||
|
||||
|
||||
<table id="tutorial_nav">
|
||||
<tr>
|
||||
<td id="previous_step">{@link tutorial.step_00 Previous}</td>
|
||||
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-1/app Live
|
||||
Demo}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">
|
||||
{@link https://github.com/angular/angular-phonecat/compare/step-0...step-1 Code Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_02 Next}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<ul doc:tutorial-nav="1"></ul>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,17 +1,11 @@
|
|||
@ngdoc overview
|
||||
@name Tutorial: Step 2
|
||||
@name Tutorial: 2 - Angular Template
|
||||
@description
|
||||
<table id="tutorial_nav">
|
||||
<tr>
|
||||
<td id="previous_step">{@link tutorial.step_01 Previous}</td>
|
||||
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-2/app Live
|
||||
Demo}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-1...step-2 Code
|
||||
Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_03 Next}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<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
|
||||
|
|
@ -24,20 +18,11 @@ code and separate concerns. With that in mind, let's use a little angular and Ja
|
|||
model, view, and controller components to our app.
|
||||
|
||||
|
||||
1. Reset your workspace to step 2.
|
||||
|
||||
|
||||
git checkout -f step-2
|
||||
<doc:tutorial-instructions step="2"></doc:tutorial-instructions>
|
||||
|
||||
|
||||
or
|
||||
|
||||
|
||||
./goto_step.sh 2
|
||||
|
||||
|
||||
2. Refresh your browser or check the app out on {@link
|
||||
http://angular.github.com/angular-phonecat/step-2/app angular's server}.
|
||||
|
||||
|
||||
The app now contains a list with 3 phones.
|
||||
|
|
@ -76,9 +61,9 @@ __`app/index.html`:__
|
|||
</pre>
|
||||
|
||||
|
||||
We replaced the hard-coded phone list with the {@link angular.widget.@ng:repeat ng:repeat widget}
|
||||
and two {@link guide.expression angular expressions} enclosed in curly braces: `{{phone.name}}` and
|
||||
`{{phone.snippet}}`:
|
||||
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
|
||||
|
|
@ -90,12 +75,12 @@ tag as the template.
|
|||
|
||||
|
||||
* The curly braces around `phone.name` and `phone.snippet` are an example of {@link
|
||||
angular.markup angular markup}. The curly markup is shorthand for the angular directive {@link
|
||||
angular.directive.ng:bind ng:bind}. The `ng:bind` directives indicate to angular that these are
|
||||
template binding points. Binding points are locations in the template where angular creates
|
||||
data-binding between the view and the model. In angular, the view is a projection of the model
|
||||
through the HTML template. This means that whenever the model changes, angular refreshes the
|
||||
appropriate binding points, which updates the view.
|
||||
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
|
||||
angular that these are template binding points. Binding points are locations in the template where
|
||||
angular creates data-binding between the view and the model. In angular, the view is a projection
|
||||
of the model through the HTML template. This means that whenever the model changes, angular
|
||||
refreshes the appropriate binding points, which updates the view.
|
||||
|
||||
|
||||
|
||||
|
|
@ -133,7 +118,7 @@ data, and logic components:
|
|||
|
||||
|
||||
* The name of our controller function (in the JavaScript file `controllers.js`) matches the
|
||||
{@link angular.directive.@ng:controller ng:controller} directive in the `<body>` tag
|
||||
{@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">`
|
||||
|
|
@ -145,7 +130,7 @@ the template, model and controller all work together. Angular uses scopes, along
|
|||
information contained in the template, data model, and controller, to keep the model and view
|
||||
separated but in sync. Any changes to the model are reflected in the view; any changes that occur
|
||||
in the view are reflected in the model. To learn more about angular scopes, see the {@link
|
||||
angular.scope angular scope documentation}.
|
||||
api/angular.scope angular scope documentation}.
|
||||
|
||||
|
||||
|
||||
|
|
@ -162,7 +147,7 @@ __`test/unit/controllersSpec.js`:__
|
|||
describe('PhoneCat controllers', function() {
|
||||
|
||||
|
||||
describe('PhoneListCtrl', function(){
|
||||
describe('PhoneListCtrl', function() {
|
||||
|
||||
|
||||
it('should create "phones" model with 3 phones', function() {
|
||||
|
|
@ -199,8 +184,8 @@ http://code.google.com/p/js-test-driver/ JsTestDriver}. To run the test, do the
|
|||
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.
|
||||
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`
|
||||
|
|
@ -218,7 +203,7 @@ the tests and report the results in the terminal.
|
|||
Yay! The test passed! Or not...
|
||||
|
||||
|
||||
Note: If you see errors after you run the test, close the browser tab and go back to the terminal
|
||||
Note: If you see errors after you run the test, close the browser tab and go back to the terminal
|
||||
and kill the script, then repeat the procedure above.
|
||||
|
||||
|
||||
|
|
@ -231,13 +216,13 @@ and kill the script, then repeat the procedure above.
|
|||
<p>Total number of phones: {{phones.length}}</p>
|
||||
|
||||
|
||||
* Create a new model property in the controller and bind to it from the template. For example:
|
||||
* Create a new model property in the controller and bind to it from the template. For example:
|
||||
|
||||
|
||||
this.hello = "Hello, World!"
|
||||
|
||||
|
||||
Refresh your browser to make sure it says, "Hello, World!"
|
||||
Refresh your browser to make sure it says, "Hello, World!"
|
||||
|
||||
|
||||
* Create a repeater that constructs a simple table:
|
||||
|
|
@ -273,17 +258,5 @@ you're testing as you go. Now, let's go to step 3 to learn how to add full text
|
|||
|
||||
|
||||
|
||||
<table id="tutorial_nav">
|
||||
<tr>
|
||||
<td id="previous_step">{@link tutorial.step_01 Previous}</td>
|
||||
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-2/app Live
|
||||
Demo}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-1...step-2 Code
|
||||
Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_03 Next}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<ul doc:tutorial-nav="2"></ul>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +1,11 @@
|
|||
@ngdoc overview
|
||||
@name Tutorial: Step 3
|
||||
@name Tutorial: 3 - Filtering Repeaters
|
||||
@description
|
||||
<table id="tutorial_nav">
|
||||
<tr>
|
||||
<td id="previous_step">{@link tutorial.step_02 Previous}</td>
|
||||
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-3/app Live Demo}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-2...step-3 Code
|
||||
Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_04 Next}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<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
|
||||
|
|
@ -19,20 +14,11 @@ because a good end-to-end test is a good friend. It stays with your app, keeps a
|
|||
quickly detects regressions.
|
||||
|
||||
|
||||
1. Reset your workspace to step 3.
|
||||
|
||||
|
||||
git checkout -f step-3
|
||||
<doc:tutorial-instructions step="3"></doc:tutorial-instructions>
|
||||
|
||||
|
||||
or
|
||||
|
||||
|
||||
./goto_step.sh 3
|
||||
|
||||
|
||||
2. Refresh your browser or check the app out on {@link
|
||||
http://angular.github.com/angular-phonecat/step-3/app angular's server}.
|
||||
|
||||
|
||||
The app now has a search box. The phone list on the page changes depending on what a user types
|
||||
|
|
@ -73,7 +59,7 @@ __`app/index.html`:__
|
|||
</pre>
|
||||
|
||||
|
||||
We added a standard HTML `<input>` tag and use angular's {@link angular.Array.filter $filter}
|
||||
We added a standard HTML `<input>` tag and use angular's {@link api/angular.array.filter $filter}
|
||||
function to process the input for the `ng:repeater`.
|
||||
|
||||
|
||||
|
|
@ -94,7 +80,7 @@ the DOM to reflect the current state of the model.
|
|||
<img src="img/tutorial/tutorial_03_final.png">
|
||||
|
||||
|
||||
* Use of `$filter`. The {@link angular.Array.filter $filter} method, uses the `query` value, to
|
||||
* Use of `$filter`. The {@link api/angular.array.filter $filter} method, uses the `query` value, to
|
||||
create a new array that contains only those records that match the `query`.
|
||||
|
||||
|
||||
|
|
@ -155,7 +141,7 @@ 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`
|
||||
`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}
|
||||
|
||||
|
||||
|
|
@ -174,20 +160,20 @@ really is that easy to set up any functional, readable, end-to-end test.
|
|||
* Let's see how we can get the current value of the `query` model to appear in the HTML page title.
|
||||
|
||||
|
||||
You might think you could just add the {{query}} to the title tag element as follows:
|
||||
You might think you could just add the {{query}} to the title tag element as follows:
|
||||
|
||||
|
||||
<title>Google Phone Gallery: {{query}}</title>
|
||||
|
||||
|
||||
However, when you reload the page, you won't see the expected result. This is because the "query"
|
||||
However, when you reload the page, you won't see the expected result. This is because the "query"
|
||||
model lives in the scope defined by the body element:
|
||||
|
||||
|
||||
<body ng:controller="PhoneListCtrl">
|
||||
|
||||
|
||||
If you want to bind to the query model from the `<title>` element, you must __move__ the
|
||||
If you want to bind to the query model from the `<title>` element, you must __move__ the
|
||||
`ng:controller` declaration to the HTML element because it is the common parent of both the body
|
||||
and title elements:
|
||||
|
||||
|
|
@ -195,23 +181,21 @@ and title elements:
|
|||
<html ng:controller="PhoneListCtrl">
|
||||
|
||||
|
||||
Be sure to *remove* the `ng:controller` declaration from the body element.
|
||||
Be sure to *remove* the `ng:controller` declaration from the body element.
|
||||
|
||||
|
||||
* Add the following end-to-end test into the `describe` block within `test/e2e/scenarios.js`:
|
||||
|
||||
|
||||
<pre>
|
||||
it('should display the current filter value within an element with id "status"', function() {
|
||||
it('should display the current filter value within an element with id "status"',
|
||||
function() {
|
||||
expect(element('#status').text()).toMatch(/Current filter: \s*$/);
|
||||
|
||||
|
||||
input('query').enter('nexus');
|
||||
|
||||
|
||||
expect(element('#status').text()).toMatch(/Current filter: nexus\s*$/);
|
||||
|
||||
|
||||
//alternative version of the last assertion that tests just the value of the binding
|
||||
using('#status').expect(binding('query')).toBe('nexus');
|
||||
});
|
||||
|
|
@ -238,14 +222,9 @@ With full text search under our belt and a test to verify it, let's go to step 4
|
|||
add sorting capability to the phone app.
|
||||
|
||||
|
||||
<table id="tutorial_nav">
|
||||
<tr>
|
||||
<td id="previous_step">{@link tutorial.step_02 Previous}</td>
|
||||
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-3/app Live Demo}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-2...step-3 Code
|
||||
Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_04 Next}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<ul doc:tutorial-nav="3"></ul>
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +1,11 @@
|
|||
@ngdoc overview
|
||||
@name Tutorial: Step 4
|
||||
@name Tutorial: 4 - Two-way Data Binding
|
||||
@description
|
||||
<table id="tutorial_nav">
|
||||
<tr>
|
||||
<td id="previous_step">{@link tutorial.step_03 Previous}</td>
|
||||
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-4/app Live Demo}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-3...step-4 Code
|
||||
Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_05 Next}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<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
|
||||
|
|
@ -20,24 +15,13 @@ the repeater, and letting the data binding magic do the rest of the work.
|
|||
|
||||
|
||||
|
||||
1. Reset your workspace to Step 4 using:
|
||||
<doc:tutorial-instructions step="4"></doc:tutorial-instructions>
|
||||
|
||||
|
||||
git checkout -f step-4
|
||||
|
||||
|
||||
or
|
||||
|
||||
|
||||
./goto_step.sh 4
|
||||
|
||||
|
||||
2. Refresh your browser or check the app out on {@link
|
||||
http://angular.github.com/angular-phonecat/step-4/app angular's server}.
|
||||
|
||||
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.
|
||||
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
|
||||
|
|
@ -87,9 +71,9 @@ two provided sorting options.
|
|||
<img src="img/tutorial/tutorial_04-06_final.png">
|
||||
|
||||
|
||||
* We then chained the `$filter` method with {@link angular.Array.orderBy `$orderBy`} method to
|
||||
* We then chained the `$filter` method with {@link api/angular.array.orderBy `$orderBy`} method to
|
||||
further process the input into the repeater. `$orderBy` is a utility method similar to {@link
|
||||
angular.Array.filter `$filter`}, but instead of filtering an array, it reorders it.
|
||||
api/angular.array.filter `$filter`}, but instead of filtering an array, it reorders it.
|
||||
|
||||
|
||||
Angular creates a two way data-binding between the select element and the `orderProp` model.
|
||||
|
|
@ -220,7 +204,7 @@ __`test/e2e/scenarios.js`:__
|
|||
it('should be possible to control phone order via the drop down select box', function() {
|
||||
|
||||
|
||||
//let's narrow the dataset to make the test assertions shorter
|
||||
// let's narrow the dataset to make the test assertions shorter
|
||||
input('query').enter('tablet');
|
||||
|
||||
|
||||
|
|
@ -270,16 +254,5 @@ services and how angular uses dependency injection.
|
|||
|
||||
|
||||
|
||||
<table id="tutorial_nav">
|
||||
<tr>
|
||||
<td id="previous_step">{@link tutorial.step_03 Previous}</td>
|
||||
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-4/app Live Demo}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-3...step-4 Code
|
||||
Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_05 Next}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<ul doc:tutorial-nav="4"></ul>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,47 +1,29 @@
|
|||
@ngdoc overview
|
||||
@name Tutorial: Step 5
|
||||
@name Tutorial: 5 - XHRs & Dependency Injection
|
||||
@description
|
||||
<table id="tutorial_nav">
|
||||
<tr>
|
||||
<td id="previous_step">{@link tutorial.step_04 Previous}</td>
|
||||
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-5/app Live Demo
|
||||
}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-4...step-5 Code
|
||||
Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_06 Next}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<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 angular.service services} called {@link
|
||||
angular.service.$xhr $xhr}. We will use angular's {@link guide.di dependency injection (DI)} to
|
||||
provide the service to the `PhoneListCtrl` controller.
|
||||
|
||||
|
||||
1. Reset your workspace to step 5.
|
||||
|
||||
|
||||
git checkout -f step-5
|
||||
|
||||
|
||||
or
|
||||
|
||||
|
||||
./goto_step.sh 5
|
||||
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.
|
||||
|
||||
|
||||
|
||||
|
||||
2. Refresh your browser or check the app out on {@link
|
||||
http://angular.github.com/angular-phonecat/step-5/app angular's server}.
|
||||
<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}:
|
||||
|
|
@ -74,16 +56,16 @@ Following is a sample of the file:
|
|||
## Controller
|
||||
|
||||
|
||||
We'll use angular's {@link angular.service.$xhr $xhr} service in our controller to make an HTTP
|
||||
We'll use angular's {@link api/angular.service.$xhr $xhr} service in our controller to make an HTTP
|
||||
request to your web server to fetch the data in the `app/phones/phones.json` file. `$xhr` is just
|
||||
one of several built-in {@link angular.service angular services} that handle common operations in
|
||||
web apps. Angular injects these services for you where you need them.
|
||||
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.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).
|
||||
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:`__
|
||||
|
|
@ -179,7 +161,7 @@ production code behind the scenes, as follows:
|
|||
describe('PhoneCat controllers', function() {
|
||||
|
||||
|
||||
describe('PhoneListCtrl', function(){
|
||||
describe('PhoneListCtrl', function() {
|
||||
var scope, $browser, ctrl;
|
||||
|
||||
|
||||
|
|
@ -188,8 +170,9 @@ describe('PhoneCat controllers', function() {
|
|||
$browser = scope.$service('$browser');
|
||||
|
||||
|
||||
$browser.xhr.expectGET('phones/phones.json').respond([{name: 'Nexus S'},
|
||||
{name: 'Motorola DROID'}]);
|
||||
$browser.xhr.expectGET('phones/phones.json')
|
||||
.respond([{name: 'Nexus S'},
|
||||
{name: 'Motorola DROID'}]);
|
||||
ctrl = scope.$new(PhoneListCtrl);
|
||||
});
|
||||
});
|
||||
|
|
@ -211,13 +194,13 @@ we create the `PhoneListCtrl` child scope, we need to tell the testing harness t
|
|||
incoming request from the controller. To do this we:
|
||||
|
||||
|
||||
* Use the {@link 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
|
||||
* 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.expectGET` method to train the `$browser` object to expect an incoming HTTP
|
||||
* Use the `$browser.xhr.expectGET` method to train the `$browser` object to expect an incoming HTTP
|
||||
request and tell it what to respond with. Note that the responses are not returned before we call
|
||||
the `$browser.xhr.flush` method.
|
||||
|
||||
|
|
@ -293,14 +276,7 @@ implementation of dependency injection), go to step 6, where you will add some t
|
|||
phones and some links.
|
||||
|
||||
|
||||
<table id="tutorial_nav">
|
||||
<tr>
|
||||
<td id="previous_step">{@link tutorial.step_04 Previous}</td>
|
||||
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-5/app Live Demo
|
||||
}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-4...step-5 Code
|
||||
Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_06 Next}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<ul doc:tutorial-nav="5"></ul>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,17 +1,11 @@
|
|||
@ngdoc overview
|
||||
@name Tutorial: Step 6
|
||||
@name Tutorial: 6 - Templating Links & Images
|
||||
@description
|
||||
<table id="tutorial_nav">
|
||||
<tr>
|
||||
<td id="previous_step">{@link tutorial.step_05 Previous}</td>
|
||||
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-6/app Live Demo
|
||||
}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-5...step-6 Code
|
||||
Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_07 Next}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<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
|
||||
|
|
@ -19,21 +13,13 @@ now, will go nowhere. In subsequent steps you will use the links to display addi
|
|||
about the phones in the catalog.
|
||||
|
||||
|
||||
1. Reset your workspace to step 6.
|
||||
|
||||
|
||||
git checkout -f step-6
|
||||
<doc:tutorial-instructions step="6"></doc:tutorial-instructions>
|
||||
|
||||
|
||||
or
|
||||
|
||||
|
||||
./goto_step.sh 6
|
||||
|
||||
|
||||
2. Refresh your browser or check the app out on {@link
|
||||
http://angular.github.com/angular-phonecat/step-6/app angular's server}.
|
||||
|
||||
You should now see links and images of the phones in the list.
|
||||
|
||||
|
||||
|
|
@ -86,16 +72,16 @@ __`app/index.html`:__
|
|||
|
||||
|
||||
To dynamically generate links that will in the future lead to phone detail pages, we used the
|
||||
now-familiar {@link angular.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.
|
||||
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
|
||||
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 an
|
||||
attribute binding in a regular `src` attribute (`<img src="{{phone.imageUrl}}">`). Using `ng:src`
|
||||
prevents the browser from making an http request to an invalid location.
|
||||
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
|
||||
an attribute binding in a regular `src` attribute (`<img src="{{phone.imageUrl}}">`). Using
|
||||
`ng:src` prevents the browser from making an http request to an invalid location.
|
||||
|
||||
|
||||
|
||||
|
|
@ -145,15 +131,5 @@ templates and how angular makes it easy to create applications that have multipl
|
|||
|
||||
|
||||
|
||||
<table id="tutorial_nav">
|
||||
<tr>
|
||||
<td id="previous_step">{@link tutorial.step_05 Previous}</td>
|
||||
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-6/app Live Demo
|
||||
}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-5...step-6 Code
|
||||
Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_07 Next}</td>
|
||||
</tr>
|
||||
</table>
|
||||
<ul doc:tutorial-nav="6"></ul>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,41 +1,26 @@
|
|||
@ngdoc overview
|
||||
@name Tutorial: Step 7
|
||||
@name Tutorial: 7 - Routing & Multiple Views
|
||||
@description
|
||||
<table id="tutorial_nav">
|
||||
<tr>
|
||||
<td id="previous_step">{@link tutorial.step_06 Previous}</td>
|
||||
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-7/app Live Demo
|
||||
}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-6...step-7 Code
|
||||
Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_08 Next}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<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.
|
||||
|
||||
|
||||
1. Reset your workspace to step 7.
|
||||
|
||||
|
||||
git checkout -f step-7
|
||||
<doc:tutorial-instructions step="7"></doc:tutorial-instructions>
|
||||
|
||||
|
||||
or
|
||||
|
||||
|
||||
./goto_step.sh 7
|
||||
|
||||
|
||||
2. Refresh your browser, but be sure that there is nothing in the url after `app/index.html`, or
|
||||
check the app out on {@link http://angular.github.com/angular-phonecat/step-7/app angular's
|
||||
server}.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
|
||||
|
||||
|
|
@ -63,9 +48,9 @@ our application. Other "partial templates" are then included into this layout te
|
|||
the current "route" — the view that is currently displayed to the user.
|
||||
|
||||
|
||||
Application routes in angular are declared via the {@link 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
|
||||
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
|
||||
http://en.wikipedia.org/wiki/Deep_linking deep linking}, which lets us utilize the browser's
|
||||
history (back and forward navigation) and bookmarks.
|
||||
|
||||
|
|
@ -82,13 +67,13 @@ function PhoneCatCtrl($route) {
|
|||
|
||||
|
||||
$route.when('/phones',
|
||||
{template: 'partials/phone-list.html', controller: PhoneListCtrl});
|
||||
{template: 'partials/phone-list.html', controller: PhoneListCtrl});
|
||||
$route.when('/phones/:phoneId',
|
||||
{template: 'partials/phone-detail.html', controller: PhoneDetailCtrl});
|
||||
{template: 'partials/phone-detail.html', controller: PhoneDetailCtrl});
|
||||
$route.otherwise({redirectTo: '/phones'});
|
||||
|
||||
|
||||
$route.onChange(function(){
|
||||
$route.onChange(function() {
|
||||
self.params = $route.current.params;
|
||||
});
|
||||
|
||||
|
|
@ -135,8 +120,8 @@ the route declaration — `'/phones/:phoneId'` — as a template that is matched
|
|||
URL. All variables defined with the `:` notation are extracted into the `$route.current.params` map.
|
||||
|
||||
|
||||
The `params` alias created in the {@link angular.service.$route `$route.onChange`} callback allows
|
||||
us to use the `phoneId` property of this map in the `phone-details.html` template.
|
||||
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.
|
||||
|
||||
|
||||
|
||||
|
|
@ -144,9 +129,9 @@ us to use the `phoneId` property of this map in the `phone-details.html` templat
|
|||
## Template
|
||||
|
||||
|
||||
The `$route` service is usually used in conjunction with the {@link angular.widget.ng:view ng:view}
|
||||
widget. The role of the `ng:view` widget is to include the view template for the current route into
|
||||
the layout template, which makes it a perfect fit for our `index.html` template.
|
||||
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`:__
|
||||
|
|
@ -279,17 +264,7 @@ With the routing set up and the phone list view implemented, we're ready to go t
|
|||
implement the phone details view.
|
||||
|
||||
|
||||
<table id="tutorial_nav">
|
||||
<tr>
|
||||
<td id="previous_step">{@link tutorial.step_06 Previous}</td>
|
||||
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-7/app Live Demo
|
||||
}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-6...step-7 Code
|
||||
Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_08 Next}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<ul doc:tutorial-nav="7"></ul>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,43 +1,29 @@
|
|||
@ngdoc overview
|
||||
@name Tutorial: Step 8
|
||||
@name Tutorial: 8 - More Templating
|
||||
@description
|
||||
<table id="tutorial_nav">
|
||||
<tr>
|
||||
<td id="previous_step">{@link tutorial.step_07 Previous}</td>
|
||||
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-8/app Live Demo
|
||||
}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-7...step-8 Code
|
||||
Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_09 Next}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<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.
|
||||
|
||||
|
||||
1. Reset your workspace to Step 8 using:
|
||||
|
||||
|
||||
git checkout -f step-8
|
||||
<doc:tutorial-instructions step="8"></doc:tutorial-instructions>
|
||||
|
||||
|
||||
or
|
||||
|
||||
|
||||
./goto_step.sh 8
|
||||
Now when you click on a phone on the list, the phone details page with phone-specific information
|
||||
is displayed.
|
||||
|
||||
|
||||
2. Refresh your browser or check the app out on {@link
|
||||
http://angular.github.com/angular-phonecat/step-8/app angular's server}.
|
||||
|
||||
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 angular.service.$xhr $xhr} to fetch our
|
||||
To implement the phone details view we will use {@link api/angular.service.$xhr $xhr} to fetch our
|
||||
data, and we'll flesh out the `phone-details.html` view template.
|
||||
|
||||
|
||||
|
|
@ -56,8 +42,7 @@ phone:
|
|||
__`app/phones/nexus-s.json`:__ (sample snippet)
|
||||
<pre>
|
||||
{
|
||||
"additionalFeatures": "Contour Display, Near Field Communications (NFC), Three-axis gyroscope,
|
||||
Anti-fingerprint display coating, Internet Calling support (VoIP/SIP)",
|
||||
"additionalFeatures": "Contour Display, Near Field Communications (NFC),...",
|
||||
"android": {
|
||||
"os": "Android 2.3",
|
||||
"ui": "Android"
|
||||
|
|
@ -172,7 +157,7 @@ step 5.
|
|||
__`test/unit/controllerSpec.js`:__
|
||||
<pre>
|
||||
...
|
||||
it('should fetch phone detail', function(){
|
||||
it('should fetch phone detail', function() {
|
||||
scope.params = {phoneId:'xyz'};
|
||||
$browser.xhr.expectGET('phones/xyz.json').respond({name:'phone xyz'});
|
||||
ctrl = scope.$new(PhoneDetailCtrl);
|
||||
|
|
@ -192,10 +177,10 @@ To run the unit tests, execute the `./scripts/test.sh` script and you should see
|
|||
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)
|
||||
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)
|
||||
|
||||
|
||||
|
||||
|
|
@ -251,17 +236,7 @@ Now that the phone details view is in place, proceed to step 9 to learn how to w
|
|||
custom display filter.
|
||||
|
||||
|
||||
<table id="tutorial_nav">
|
||||
<tr>
|
||||
<td id="previous_step">{@link tutorial.step_07 Previous}</td>
|
||||
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-8/app Live Demo
|
||||
}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-7...step-8 Code
|
||||
Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_09 Next}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<ul doc:tutorial-nav="8"></ul>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,37 +1,23 @@
|
|||
@ngdoc overview
|
||||
@name Tutorial: Step 9
|
||||
@name Tutorial: 9 - Filters
|
||||
@description
|
||||
<table id="tutorial_nav">
|
||||
<tr>
|
||||
<td id="previous_step">{@link tutorial.step_08 Previous}</td>
|
||||
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-9/app Live Demo
|
||||
}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-8...step-9 Code
|
||||
Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_10 Next}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<ul doc:tutorial-nav="9"></ul>
|
||||
|
||||
|
||||
|
||||
|
||||
In this step you will learn how to create your own custom display filter.
|
||||
|
||||
|
||||
1. Reset your workspace to Step 9 using:
|
||||
|
||||
|
||||
git checkout -f step-9
|
||||
<doc:tutorial-instructions step="9"></doc:tutorial-instructions>
|
||||
|
||||
|
||||
or
|
||||
|
||||
|
||||
./goto_step.sh 9
|
||||
|
||||
|
||||
2. Refresh your browser or check the app out on {@link
|
||||
http://angular.github.com/angular-phonecat/step-9/app angular's server}.
|
||||
|
||||
Navigate to one of the detail pages.
|
||||
|
||||
|
||||
|
|
@ -51,7 +37,7 @@ GitHub}:
|
|||
|
||||
|
||||
In order to create a new filter, simply register your custom filter function with the {@link
|
||||
angular.filter `angular.filter`} API.
|
||||
api/angular.filter `angular.filter`} API.
|
||||
|
||||
|
||||
__`app/js/filters.js`:__
|
||||
|
|
@ -146,7 +132,7 @@ output.
|
|||
# Experiments
|
||||
|
||||
|
||||
* Let's experiment with some of the {@link angular.filter built-in angular filters} and add the
|
||||
* Let's experiment with some of the {@link api/angular.filter built-in angular filters} and add the
|
||||
following bindings to `index.html`:
|
||||
* `{{ "lower cap string" | uppercase }}`
|
||||
* `{{ {foo: "bar", baz: 23} | json }}`
|
||||
|
|
@ -170,14 +156,6 @@ Now that you have learned how to write and test a custom filter, go to step 10 t
|
|||
use angular to enhance the phone details page further.
|
||||
|
||||
|
||||
<table id="tutorial_nav">
|
||||
<tr>
|
||||
<td id="previous_step">{@link tutorial.step_08 Previous}</td>
|
||||
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-9/app Live Demo
|
||||
}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-8...step-9 Code
|
||||
Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_10 Next}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<ul doc:tutorial-nav="9"></ul>
|
||||
|
|
|
|||
|
|
@ -1,36 +1,21 @@
|
|||
@ngdoc overview
|
||||
@name Tutorial: Step 10
|
||||
@name Tutorial: 10 - Event Handlers
|
||||
@description
|
||||
<table id="tutorial_nav">
|
||||
<tr>
|
||||
<td id="previous_step">{@link tutorial.step_09 Previous}</td>
|
||||
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-10/app Live Demo
|
||||
}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-9...step-10 Code
|
||||
Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_11 Next}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<ul doc:tutorial-nav="10"></ul>
|
||||
|
||||
|
||||
|
||||
|
||||
In this step, you will add a clickable phone image swapper to the phone details page.
|
||||
|
||||
|
||||
1. Reset your workspace to Step 10 using:
|
||||
|
||||
|
||||
git checkout -f step-10
|
||||
<doc:tutorial-instructions step="10"></doc:tutorial-instructions>
|
||||
|
||||
|
||||
or
|
||||
|
||||
|
||||
./goto_step.sh 10
|
||||
|
||||
|
||||
2. Refresh your browser or check the app out on {@link
|
||||
http://angular.github.com/angular-phonecat/step-10/app angular's server}.
|
||||
|
||||
|
||||
The phone details view displays one large image of the current phone and several smaller thumbnail
|
||||
|
|
@ -103,9 +88,10 @@ __`app/partials/phone-detail.html`:__
|
|||
We bound the `ng:src` attribute of the large image to the `mainImageUrl` property.
|
||||
|
||||
|
||||
We also registered an {@link 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.
|
||||
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">
|
||||
|
|
@ -197,14 +183,6 @@ With the phone image swapper in place, we're ready for step 11 (the last step!)
|
|||
better way to fetch data.
|
||||
|
||||
|
||||
<table id="tutorial_nav">
|
||||
<tr>
|
||||
<td id="previous_step">{@link tutorial.step_09 Previous}</td>
|
||||
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-10/app Live Demo
|
||||
}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-9...step-10 Code
|
||||
Diff}</td>
|
||||
<td id="next_step">{@link tutorial.step_11 Next}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<ul doc:tutorial-nav="10"></ul>
|
||||
|
|
|
|||
|
|
@ -1,36 +1,19 @@
|
|||
@ngdoc overview
|
||||
@name Tutorial: Step 11
|
||||
@name Tutorial: 11 - REST and Custom Services
|
||||
@description
|
||||
<table id="tutorial_nav">
|
||||
<tr>
|
||||
<td id="previous_step">{@link tutorial.step_10 Previous}</td>
|
||||
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-11/app Live Demo
|
||||
}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-10...step-11
|
||||
Code Diff}</td>
|
||||
<td id="next_step">Next</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<ul doc:tutorial-nav="11"></ul>
|
||||
|
||||
|
||||
|
||||
|
||||
In this step, you will improve the way our app fetches data.
|
||||
|
||||
|
||||
1. Reset your workspace to Step 11 using:
|
||||
|
||||
|
||||
git checkout -f step-11
|
||||
|
||||
|
||||
or
|
||||
|
||||
|
||||
./goto_step.sh 11
|
||||
|
||||
|
||||
2. Refresh your browser or check the app out on {@link
|
||||
http://angular.github.com/angular-phonecat/step-11/app angular's server}.
|
||||
<doc:tutorial-instructions step="11"></doc:tutorial-instructions>
|
||||
|
||||
|
||||
|
||||
|
|
@ -38,7 +21,7 @@ http://angular.github.com/angular-phonecat/step-11/app angular's server}.
|
|||
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
|
||||
angular.service.$xhr $xhr} API, HTTP methods and URLs.
|
||||
api/angular.service.$xhr $xhr} API, HTTP methods and URLs.
|
||||
|
||||
|
||||
The most important changes are listed below. You can see the full diff on {@link
|
||||
|
|
@ -68,23 +51,24 @@ __`app/index.html`.__
|
|||
|
||||
__`app/js/services.js`.__
|
||||
<pre>
|
||||
angular.service('Phone', function($resource){
|
||||
angular.service('Phone', function($resource) {
|
||||
return $resource('phones/:phoneId.json', {}, {
|
||||
query: {method:'GET', params:{phoneId:'phones'}, isArray:true}
|
||||
query: {method: 'GET', params: {phoneId: 'phones'}, isArray: true}
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
|
||||
|
||||
We used the {@link 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
|
||||
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 angular.service.$resource `$resource`} service makes it easy to create a {@link
|
||||
The {@link api/angular.service.$resource `$resource`} service makes it easy to create a {@link
|
||||
http://en.wikipedia.org/wiki/Representational_State_Transfer RESTful} client with just a few lines
|
||||
of code. This client can then be used in our application, instead of the lower-level `$xhr` service.
|
||||
of code. This client can then be used in our application, instead of the lower-level {@link
|
||||
api/angular.service.$xhr $xhr} service.
|
||||
|
||||
|
||||
|
||||
|
|
@ -93,10 +77,10 @@ of code. This client can then be used in our application, instead of the lower-l
|
|||
|
||||
|
||||
We simplified our sub-controllers (`PhoneListCtrl` and `PhoneDetailCtrl`) by factoring out the
|
||||
lower-level `$xhr` service, replacing it with a new service called `Phone`. Angular's {@link
|
||||
angular.service.$resource `$resource`} service is easier to use than `$xhr` for interacting with
|
||||
data sources exposed as RESTful resources. It is also easier now to understand what the code in our
|
||||
controllers is doing.
|
||||
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`.__
|
||||
|
|
@ -168,12 +152,13 @@ processing them as expected. The tests also check that our controllers are inter
|
|||
service correctly.
|
||||
|
||||
|
||||
The `$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 problem, we use a newly-defined
|
||||
`toEqualData` {@link http://pivotal.github.com/jasmine/jsdoc/symbols/jasmine.Matchers.html Jasmine
|
||||
matcher}. When the `toEqualData` matcher compares two objects, it takes only object properties into
|
||||
account and ignores methods.
|
||||
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
|
||||
problem, we use a newly-defined `toEqualData` {@link
|
||||
http://pivotal.github.com/jasmine/jsdoc/symbols/jasmine.Matchers.html Jasmine matcher}. When the
|
||||
`toEqualData` matcher compares two objects, it takes only object properties into account and
|
||||
ignores methods.
|
||||
|
||||
|
||||
|
||||
|
|
@ -192,7 +177,7 @@ describe('PhoneCat controllers', function() {
|
|||
});
|
||||
|
||||
|
||||
describe('PhoneListCtrl', function(){
|
||||
describe('PhoneListCtrl', function() {
|
||||
var scope, $browser, ctrl;
|
||||
|
||||
|
||||
|
|
@ -201,8 +186,8 @@ describe('PhoneCat controllers', function() {
|
|||
$browser = scope.$service('$browser');
|
||||
|
||||
|
||||
$browser.xhr.expectGET('phones/phones.json').respond([{name: 'Nexus S'},
|
||||
{name: 'Motorola DROID'}]);
|
||||
$browser.xhr.expectGET('phones/phones.json')
|
||||
.respond([{name: 'Nexus S'}, {name: 'Motorola DROID'}]);
|
||||
ctrl = scope.$new(PhoneListCtrl);
|
||||
});
|
||||
|
||||
|
|
@ -225,7 +210,7 @@ describe('PhoneCat controllers', function() {
|
|||
|
||||
|
||||
|
||||
describe('PhoneDetailCtrl', function(){
|
||||
describe('PhoneDetailCtrl', function() {
|
||||
var scope, $browser, ctrl;
|
||||
|
||||
|
||||
|
|
@ -241,7 +226,7 @@ describe('PhoneCat controllers', function() {
|
|||
});
|
||||
|
||||
|
||||
it('should fetch phone detail', function(){
|
||||
it('should fetch phone detail', function() {
|
||||
scope.params = {phoneId:'xyz'};
|
||||
$browser.xhr.expectGET('phones/xyz.json').respond({name:'phone xyz'});
|
||||
ctrl = scope.$new(PhoneDetailCtrl);
|
||||
|
|
@ -262,10 +247,10 @@ To run the unit tests, execute the `./scripts/test.sh` script and you should see
|
|||
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)
|
||||
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)
|
||||
|
||||
|
||||
|
||||
|
|
@ -278,17 +263,5 @@ There you have it! We have created a web app in a relatively short amount of ti
|
|||
|
||||
|
||||
|
||||
<table id="tutorial_nav">
|
||||
<tr>
|
||||
<td id="previous_step">{@link tutorial.step_10 Previous}</td>
|
||||
<td id="step_result">{@link http://angular.github.com/angular-phonecat/step-11/app Live Demo
|
||||
}</td>
|
||||
<td id="tut_home">{@link tutorial Tutorial Home}</td>
|
||||
<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-10...step-11
|
||||
Code Diff}</td>
|
||||
<td id="next_step">{@link tutorial/the_end Next}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<ul doc:tutorial-nav="11"></ul>
|
||||
|
||||
|
|
|
|||
|
|
@ -2,20 +2,26 @@
|
|||
@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}.
|
||||
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 intro.contribute contributing} to angular.
|
||||
own, and that you might be interested in {@link misc/contribute contributing} to angular.
|
||||
|
||||
|
||||
If you have questions or feedback or just want to say "hi", please post a message at {@link
|
||||
https://groups.google.com/forum/#!forum/angular}.
|
||||
|
|
|
|||
Loading…
Reference in a new issue