mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-16 23:30:23 +00:00
allow documentation to be in external file
* Load templates once instead of per request * show timing information * load files ending in .ngdoc and process them
This commit is contained in:
parent
2bbced212e
commit
58d0e8945d
11 changed files with 646 additions and 615 deletions
53
docs/angular.directive.ngdoc
Normal file
53
docs/angular.directive.ngdoc
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name angular.directive
|
||||
@namespace Namespace for all directives.
|
||||
|
||||
@description
|
||||
A directive is an HTML attribute that you can use in an existing HTML element type or in a
|
||||
DOM element type that you create as {@link angular.widget}, to modify that element's
|
||||
properties. You can use any number of directives per element.
|
||||
|
||||
For example, you can add the ng:bind directive as an attribute of an HTML span element, as in
|
||||
`<span ng:bind="1+2"></span>`. How does this work? The compiler passes the attribute value
|
||||
`1+2` to the ng:bind extension, which in turn tells the {@link angular.scope} to watch that
|
||||
expression and report changes. On any change it sets the span text to the expression value.
|
||||
|
||||
Here's how to define {@link angular.directive.ng:bind ng:bind}:
|
||||
<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>
|
||||
|
||||
# Directive vs. Attribute Widget
|
||||
Both [attribute widgets](#!angular.widget) and directives can compile a DOM element
|
||||
attribute. So why have two different ways to do the same thing? The answer is that order
|
||||
matters, but we have no control over the order in which attributes are read. To solve this
|
||||
we apply attribute widget before the directive.
|
||||
|
||||
For example, consider this piece of HTML, which uses the directives `ng:repeat`, `ng:init`,
|
||||
and `ng:bind`:
|
||||
<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. We need to execute
|
||||
{@link angular.directive.ng:repeat ng:repeat} before we run the
|
||||
{@link angular.directive.ng:init ng:init} and `ng:bind` on the `<li/>;`. This is because we
|
||||
want to run the `ng:init="a=a+1` and `ng:bind="person"` once for each person in people. We
|
||||
could not have used directive to create this template because attributes are read in an
|
||||
unspecified order and there is no way of guaranteeing that the repeater attribute would
|
||||
execute first. Using the `ng:repeat` attribute directive ensures that we can transform the
|
||||
DOM element into a template.
|
||||
|
||||
Widgets run before directives. Widgets may manipulate the DOM whereas directives are not
|
||||
expected to do so, and so they run last.
|
||||
43
docs/angular.element.ngdoc
Normal file
43
docs/angular.element.ngdoc
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
@workInProgress
|
||||
@ngdoc function
|
||||
@name angular.element
|
||||
@function
|
||||
|
||||
@description
|
||||
Wraps a raw DOM element or HTML string as [jQuery](http://jquery.com) element.
|
||||
`angular.element` is either an alias for [jQuery](http://api.jquery.com/jQuery/) function if
|
||||
jQuery is loaded or a function that wraps the element or string in angular's jQuery lite
|
||||
implementation.
|
||||
|
||||
Real jQuery always takes precedence if it was loaded before angular.
|
||||
|
||||
Angular's jQuery lite implementation is a tiny API-compatible subset of jQuery which allows
|
||||
angular to manipulate DOM. The functions implemented are usually just the basic versions of
|
||||
them and might not support arguments and invocation styles.
|
||||
|
||||
NOTE: All element references in angular are always wrapped with jQuery (lite) and are never
|
||||
raw DOM references.
|
||||
|
||||
Angular's jQuery lite implements these functions:
|
||||
|
||||
- [addClass()](http://api.jquery.com/addClass/)
|
||||
- [after()](http://api.jquery.com/after/)
|
||||
- [append()](http://api.jquery.com/append/)
|
||||
- [attr()](http://api.jquery.com/attr/)
|
||||
- [bind()](http://api.jquery.com/bind/)
|
||||
- [children()](http://api.jquery.com/children/)
|
||||
- [clone()](http://api.jquery.com/clone/)
|
||||
- [css()](http://api.jquery.com/css/)
|
||||
- [data()](http://api.jquery.com/data/)
|
||||
- [hasClass()](http://api.jquery.com/hasClass/)
|
||||
- [parent()](http://api.jquery.com/parent/)
|
||||
- [remove()](http://api.jquery.com/remove/)
|
||||
- [removeAttr()](http://api.jquery.com/removeAttr/)
|
||||
- [removeClass()](http://api.jquery.com/removeClass/)
|
||||
- [removeData()](http://api.jquery.com/removeData/)
|
||||
- [replaceWith()](http://api.jquery.com/replaceWith/)
|
||||
- [text()](http://api.jquery.com/text/)
|
||||
- [trigger()](http://api.jquery.com/trigger/)
|
||||
|
||||
@param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery.
|
||||
@returns {Object} jQuery object.
|
||||
76
docs/angular.filter.ngdoc
Normal file
76
docs/angular.filter.ngdoc
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name angular.filter
|
||||
@namespace Namespace for all filters.
|
||||
@description
|
||||
# Overview
|
||||
Filters are a standard way to format your data for display to the user. For example, you
|
||||
might have the number 1234.5678 and would like to display it as US currency: $1,234.57.
|
||||
Filters allow you to do just that. In addition to transforming the data, filters also modify
|
||||
the DOM. This allows the filters to for example apply css styles to the filtered output if
|
||||
certain conditions were met.
|
||||
|
||||
|
||||
# Standard Filters
|
||||
|
||||
The Angular framework provides a standard set of filters for common operations, including:
|
||||
{@link angular.filter.currency currency}, {@link angular.filter.json json},
|
||||
{@link angular.filter.number number}, and {@link angular.filter.html html}. You can also add
|
||||
your own filters.
|
||||
|
||||
|
||||
# Syntax
|
||||
|
||||
Filters can be part of any {@link angular.scope} evaluation but are typically used with
|
||||
{{bindings}}. Filters typically transform the data to a new data type, formating the data in
|
||||
the process. Filters can be chained and take optional arguments. Here are few examples:
|
||||
|
||||
* 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. To number, the argument “5” requests 5 digits
|
||||
to the right of the decimal point.
|
||||
|
||||
|
||||
# Writing your own Filters
|
||||
|
||||
Writing your own filter is very easy: just define a JavaScript function on `angular.filter`.
|
||||
The framework passes in the input value as the first argument to your function. Any filter
|
||||
arguments are passed in as additional function arguments.
|
||||
|
||||
You can use these variables in the function:
|
||||
|
||||
* `this` — The current scope.
|
||||
* `this.$element` — The DOM element containing the binding. This allows the filter to manipulate
|
||||
the DOM in addition to transforming the input.
|
||||
|
||||
|
||||
@exampleDescription
|
||||
The following example filter reverses a text string. In addition, it conditionally makes the
|
||||
text upper-case (to demonstrate optional arguments) and assigns color (to demonstrate DOM
|
||||
modification).
|
||||
|
||||
@example
|
||||
<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;
|
||||
}
|
||||
if (uppercase) {
|
||||
out = out.toUpperCase();
|
||||
}
|
||||
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"}}
|
||||
|
||||
78
docs/angular.formatter.ngdoc
Normal file
78
docs/angular.formatter.ngdoc
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name angular.formatter
|
||||
@namespace Namespace for all formats.
|
||||
@description
|
||||
# Overview
|
||||
The formatters are responsible for translating user readable text in an input widget to a
|
||||
data model stored in an application.
|
||||
|
||||
# Writting your own Formatter
|
||||
Writing your own formatter is easy. Just register a pair of JavaScript functions with
|
||||
`angular.formatter`. One function for parsing user input text to the stored form,
|
||||
and one for formatting the stored data to user-visible text.
|
||||
|
||||
Here is an example of a "reverse" formatter: The data is stored in uppercase and in
|
||||
reverse, while it is displayed in lower case and non-reversed. User edits are
|
||||
automatically parsed into the internal form and data changes are automatically
|
||||
formatted to the viewed form.
|
||||
|
||||
<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>
|
||||
|
||||
@example
|
||||
<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>
|
||||
|
||||
Formatted:
|
||||
<input type="text" name="data" value="angular" ng:format="reverse"/>
|
||||
<br/>
|
||||
|
||||
Stored:
|
||||
<input type="text" name="data"/><br/>
|
||||
<pre>{{data}}</pre>
|
||||
|
||||
|
||||
@scenario
|
||||
it('should store reverse', function(){
|
||||
expect(element('.doc-example input:first').val()).toEqual('angular');
|
||||
expect(element('.doc-example input:last').val()).toEqual('RALUGNA');
|
||||
|
||||
this.addFutureAction('change to XYZ', function($window, $document, done){
|
||||
$document.elements('.doc-example input:last').val('XYZ').trigger('change');
|
||||
done();
|
||||
});
|
||||
expect(element('.doc-example input:first').val()).toEqual('zyx');
|
||||
});
|
||||
4
docs/angular.ngdoc
Normal file
4
docs/angular.ngdoc
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name angular
|
||||
@namespace The exported angular namespace.
|
||||
159
docs/angular.service.ngdoc
Normal file
159
docs/angular.service.ngdoc
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name angular.service
|
||||
|
||||
@description
|
||||
# Overview
|
||||
Services are substituable objects, which are wired together using dependency injection.
|
||||
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 satisfy these dependencies, so each service must explicitely define its dependencies.
|
||||
This is done by `$inject` property.
|
||||
|
||||
For now, life time of all services is the same as the life time of page.
|
||||
|
||||
|
||||
# Built-in services
|
||||
The Angular framework provides a standard set of services for common operations.
|
||||
You can write your own services and rewrite these standard services as well.
|
||||
Like other core angular variables, the built-in services always start with $.
|
||||
|
||||
* `angular.service.$browser`
|
||||
* `angular.service.$window`
|
||||
* `angular.service.$document`
|
||||
* `angular.service.$location`
|
||||
* `angular.service.$log`
|
||||
* `angular.service.$exceptionHandler`
|
||||
* `angular.service.$hover`
|
||||
* `angular.service.$invalidWidgets`
|
||||
* `angular.service.$route`
|
||||
* `angular.service.$xhr`
|
||||
* `angular.service.$xhr.error`
|
||||
* `angular.service.$xhr.bulk`
|
||||
* `angular.service.$xhr.cache`
|
||||
* `angular.service.$resource`
|
||||
* `angular.service.$cookies`
|
||||
* `angular.service.$cookieStore`
|
||||
|
||||
# Writing your own custom services
|
||||
Angular provides only set of basic services, so you will probably need to write your custom
|
||||
service very soon. To do so, you need to write a factory function and register this function
|
||||
to angular's dependency injector. This factory function must return an object - your service
|
||||
(it is not called with new operator).
|
||||
|
||||
**angular.service** has three parameters:
|
||||
|
||||
- `{string} name` - Name of the service
|
||||
- `{function()} factory` - Factory function (called just once by DI)
|
||||
- `{Object} config` - Hash of configuration (`$inject`, `$creation`)
|
||||
|
||||
If your service requires - depends on other services, you need to specify them
|
||||
in config hash - property $inject. This property is an array of strings (service names).
|
||||
These dependencies will be passed as parameters to the factory function by DI.
|
||||
This approach is very useful when testing, as you can inject mocks/stubs/dummies.
|
||||
|
||||
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 in a controllers is very similar to using service in other service.
|
||||
Again, we will use dependency injection.
|
||||
|
||||
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
|
||||
<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>
|
||||
73
docs/angular.validator.ngdoc
Normal file
73
docs/angular.validator.ngdoc
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name angular.validator
|
||||
@namespace Namespace for all filters.
|
||||
@description
|
||||
# Overview
|
||||
Validators are a standard way to check the user input against a specific criteria. For
|
||||
example, you might need to check that an input field contains a well-formed phone number.
|
||||
|
||||
# Syntax
|
||||
Attach a validator on user input widgets using the `ng:validate` attribute.
|
||||
|
||||
<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>
|
||||
|
||||
|
||||
# Writing your own Validators
|
||||
Writing your own validator is easy. To make a function available as a
|
||||
validator, just define the JavaScript function on the `angular.validator`
|
||||
object. <angular/> passes in the input to validate as the first argument
|
||||
to your function. Any additional validator arguments are passed in as
|
||||
additional arguments to your function.
|
||||
|
||||
You can use these variables in the function:
|
||||
|
||||
* `this` — The current scope.
|
||||
* `this.$element` — The DOM element containing the binding. This allows the filter to manipulate
|
||||
the DOM in addition to transforming the input.
|
||||
|
||||
In this example we have written a upsTrackingNo validator.
|
||||
It marks the input text "valid" only when the user enters a well-formed
|
||||
UPS tracking number.
|
||||
|
||||
@css ng-validation-error
|
||||
When validation fails, this css class is applied to the binding, making its borders red by
|
||||
default.
|
||||
|
||||
@example
|
||||
<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"/>
|
||||
|
||||
@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/);
|
||||
});
|
||||
73
docs/angular.widget.ngdoc
Normal file
73
docs/angular.widget.ngdoc
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name angular.widget
|
||||
@namespace Namespace for all widgets.
|
||||
@description
|
||||
# Overview
|
||||
Widgets allow you to create DOM elements that the browser doesn't
|
||||
already understand. You create the widget in your namespace and
|
||||
assign it behavior. You can only bind one widget per DOM element
|
||||
(unlike directives, in which you can use any number per DOM
|
||||
element). Widgets are expected to manipulate the DOM tree by
|
||||
adding new elements whereas directives are expected to only modify
|
||||
element properties.
|
||||
|
||||
Widgets come in two flavors: element and attribute.
|
||||
|
||||
# Element Widget
|
||||
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>
|
||||
<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>
|
||||
|
||||
# Attribute Widget
|
||||
Let's implement the same widget, but this time as an attribute
|
||||
that can be added to any existing DOM element.
|
||||
<pre>
|
||||
<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>
|
||||
|
||||
@example
|
||||
<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>
|
||||
|
||||
112
docs/collect.js
112
docs/collect.js
|
|
@ -16,39 +16,29 @@ var keywordPages = [];
|
|||
var SRC_DIR = "docs/";
|
||||
var OUTPUT_DIR = "build/docs/";
|
||||
var NEW_LINE = /\n\r?/;
|
||||
var TEMPLATES = {};
|
||||
var start = now();
|
||||
|
||||
function now(){ return new Date().getTime(); }
|
||||
var work = callback.chain(function () {
|
||||
console.log('Parsing Angular Reference Documentation');
|
||||
mkdirPath(OUTPUT_DIR, work.waitFor(function(){
|
||||
findJsFiles('src', work.waitMany(function(file) {
|
||||
//console.log('reading', file, '...');
|
||||
findNgDoc(file, work.waitMany(function(doc) {
|
||||
parseNgDoc(doc);
|
||||
processNgDoc(documentation, doc);
|
||||
}));
|
||||
findJsFiles('src', work.waitMany(function(file) {
|
||||
//console.log('reading', file, '...');
|
||||
findNgDocInJsFile(file, work.waitMany(function(doc) {
|
||||
parseNgDoc(doc);
|
||||
processNgDoc(documentation, doc);
|
||||
}));
|
||||
}));
|
||||
findNgDocInDir(SRC_DIR, work.waitMany(function(doc){
|
||||
parseNgDoc(doc);
|
||||
processNgDoc(documentation, doc);
|
||||
}));
|
||||
loadTemplates(TEMPLATES, work.waitFor());
|
||||
mkdirPath(OUTPUT_DIR, work.waitFor());
|
||||
}).onError(function(err){
|
||||
console.log('ERROR:', err.stack || err);
|
||||
}).onDone(function(){
|
||||
keywordPages.sort(function(a,b){
|
||||
// supper ugly comparator that orders all utility methods and objects before all the other stuff
|
||||
// like widgets, directives, services, etc.
|
||||
// Mother of all beatiful code please forgive me for the sin that this code certainly is.
|
||||
|
||||
if (a.name === b.name) return 0;
|
||||
if (a.name === 'angular') return -1;
|
||||
if (b.name === 'angular') return 1;
|
||||
|
||||
function namespacedName(page) {
|
||||
return (page.name.match(/\./g).length === 1 && page.type !== 'overview' ? '0' : '1') + page.name;
|
||||
}
|
||||
|
||||
var namespacedA = namespacedName(a),
|
||||
namespacedB = namespacedName(b);
|
||||
|
||||
return namespacedA < namespacedB ? -1 : 1;
|
||||
});
|
||||
keywordPages.sort(keywordSort);
|
||||
writeDoc(documentation.pages);
|
||||
mergeTemplate('docs-data.js', 'docs-data.js', {JSON:JSON.stringify(keywordPages)}, callback.chain());
|
||||
mergeTemplate('docs-scenario.js', 'docs-scenario.js', documentation, callback.chain());
|
||||
|
|
@ -58,7 +48,7 @@ var work = callback.chain(function () {
|
|||
mergeTemplate('docs.js', 'docs.js', documentation, callback.chain());
|
||||
mergeTemplate('doc_widgets.css', 'doc_widgets.css', documentation, callback.chain());
|
||||
mergeTemplate('doc_widgets.js', 'doc_widgets.js', documentation, callback.chain());
|
||||
console.log('DONE');
|
||||
console.log('DONE', now() - start, 'ms.');
|
||||
});
|
||||
if (!this.testmode) work();
|
||||
////////////////////
|
||||
|
|
@ -163,7 +153,7 @@ function markdownTag(doc, name, value) {
|
|||
replace(/\<\/pre\>/gmi, '</pre></div>');
|
||||
}
|
||||
|
||||
R_LINK = /{@link ([^\s}]+)((\s|\n)+(.+?))?\s*}/m
|
||||
var R_LINK = /{@link ([^\s}]+)((\s|\n)+(.+?))?\s*}/m;
|
||||
// 1 123 3 4 42
|
||||
|
||||
function markdown(text) {
|
||||
|
|
@ -313,7 +303,7 @@ function parseNgDoc(doc){
|
|||
}
|
||||
}
|
||||
|
||||
function findNgDoc(file, callback) {
|
||||
function findNgDocInJsFile(file, callback) {
|
||||
fs.readFile(file, callback.waitFor(function(err, content){
|
||||
var lines = content.toString().split(NEW_LINE);
|
||||
var doc;
|
||||
|
|
@ -346,6 +336,22 @@ function findNgDoc(file, callback) {
|
|||
}));
|
||||
}
|
||||
|
||||
function loadTemplates(cache, callback){
|
||||
fs.readdir('docs', callback.waitFor(function(err, files){
|
||||
if (err) return this.error(err);
|
||||
files.forEach(function(file){
|
||||
var match = file.match(/^(.*)\.template$/);
|
||||
if (match) {
|
||||
fs.readFile(SRC_DIR + file, callback.waitFor(function(err, content){
|
||||
if (err) return this.error(err);
|
||||
cache[match[1]] = content.toString();
|
||||
}));
|
||||
}
|
||||
});
|
||||
callback();
|
||||
}));
|
||||
};
|
||||
|
||||
function findJsFiles(dir, callback){
|
||||
fs.readdir(dir, callback.waitFor(function(err, files){
|
||||
if (err) return this.error(err);
|
||||
|
|
@ -365,7 +371,7 @@ function findJsFiles(dir, callback){
|
|||
|
||||
function processNgDoc(documentation, doc) {
|
||||
if (!doc.ngdoc) return;
|
||||
console.log('Found:', doc.ngdoc + ':' + doc.name);
|
||||
//console.log('Found:', doc.ngdoc + ':' + doc.name);
|
||||
|
||||
documentation.byName[doc.name] = doc;
|
||||
|
||||
|
|
@ -385,10 +391,50 @@ function processNgDoc(documentation, doc) {
|
|||
}
|
||||
}
|
||||
|
||||
function writeDoc(pages) {
|
||||
function writeDoc(pages, callback) {
|
||||
pages.forEach(function(doc) {
|
||||
mergeTemplate(
|
||||
doc.ngdoc + '.template',
|
||||
doc.name + '.html', doc, callback.chain());
|
||||
var template = TEMPLATES[doc.ngdoc];
|
||||
if (!template) throw new Error("No template for:" + doc.ngdoc);
|
||||
var content = mustache.to_html(template, doc);
|
||||
fs.writeFile(OUTPUT_DIR + doc.name + '.html', content, callback);
|
||||
});
|
||||
}
|
||||
|
||||
function findNgDocInDir(directory, docNotify) {
|
||||
fs.readdir(directory, docNotify.waitFor(function(err, files){
|
||||
if (err) return this.error(err);
|
||||
files.forEach(function(file){
|
||||
console.log(file);
|
||||
if (!file.match(/\.ngdoc$/)) return;
|
||||
fs.readFile(directory + file, docNotify.waitFor(function(err, content){
|
||||
if (err) return this.error(err);
|
||||
docNotify({
|
||||
raw:{
|
||||
text:content.toString(),
|
||||
file: directory + file,
|
||||
line: 1}
|
||||
});
|
||||
}));
|
||||
});
|
||||
docNotify.done();
|
||||
}));
|
||||
}
|
||||
|
||||
function keywordSort(a,b){
|
||||
// supper ugly comparator that orders all utility methods and objects before all the other stuff
|
||||
// like widgets, directives, services, etc.
|
||||
// Mother of all beautiful code please forgive me for the sin that this code certainly is.
|
||||
|
||||
if (a.name === b.name) return 0;
|
||||
if (a.name === 'angular') return -1;
|
||||
if (b.name === 'angular') return 1;
|
||||
|
||||
function namespacedName(page) {
|
||||
return (page.name.match(/\./g).length === 1 && page.type !== 'overview' ? '0' : '1') + page.name;
|
||||
}
|
||||
|
||||
var namespacedA = namespacedName(a),
|
||||
namespacedB = namespacedName(b);
|
||||
|
||||
return namespacedA < namespacedB ? -1 : 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
<script src="doc_widgets.js"></script>
|
||||
<script src="docs-data.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<body style="display:none;" ng:show="true">
|
||||
<div id="header">
|
||||
<h1>
|
||||
<span class="main-title">{{getTitle()}}</span>
|
||||
|
|
|
|||
588
src/Angular.js
588
src/Angular.js
|
|
@ -87,599 +87,25 @@ var _undefined = undefined,
|
|||
_ = window['_'],
|
||||
/** holds major version number for IE or NaN for real browsers */
|
||||
msie = parseInt((/msie (\d+)/.exec(lowercase(navigator.userAgent)) || [])[1], 10),
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc function
|
||||
* @name angular.element
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Wraps a raw DOM element or HTML string as [jQuery](http://jquery.com) element.
|
||||
* `angular.element` is either an alias for [jQuery](http://api.jquery.com/jQuery/) function if
|
||||
* jQuery is loaded or a function that wraps the element or string in angular's jQuery lite
|
||||
* implementation.
|
||||
*
|
||||
* Real jQuery always takes precedence if it was loaded before angular.
|
||||
*
|
||||
* Angular's jQuery lite implementation is a tiny API-compatible subset of jQuery which allows
|
||||
* angular to manipulate DOM. The functions implemented are usually just the basic versions of
|
||||
* them and might not support arguments and invocation styles.
|
||||
*
|
||||
* NOTE: All element references in angular are always wrapped with jQuery (lite) and are never
|
||||
* raw DOM references.
|
||||
*
|
||||
* Angular's jQuery lite implements these functions:
|
||||
*
|
||||
* - [addClass()](http://api.jquery.com/addClass/)
|
||||
* - [after()](http://api.jquery.com/after/)
|
||||
* - [append()](http://api.jquery.com/append/)
|
||||
* - [attr()](http://api.jquery.com/attr/)
|
||||
* - [bind()](http://api.jquery.com/bind/)
|
||||
* - [children()](http://api.jquery.com/children/)
|
||||
* - [clone()](http://api.jquery.com/clone/)
|
||||
* - [css()](http://api.jquery.com/css/)
|
||||
* - [data()](http://api.jquery.com/data/)
|
||||
* - [hasClass()](http://api.jquery.com/hasClass/)
|
||||
* - [parent()](http://api.jquery.com/parent/)
|
||||
* - [remove()](http://api.jquery.com/remove/)
|
||||
* - [removeAttr()](http://api.jquery.com/removeAttr/)
|
||||
* - [removeClass()](http://api.jquery.com/removeClass/)
|
||||
* - [removeData()](http://api.jquery.com/removeData/)
|
||||
* - [replaceWith()](http://api.jquery.com/replaceWith/)
|
||||
* - [text()](http://api.jquery.com/text/)
|
||||
* - [trigger()](http://api.jquery.com/trigger/)
|
||||
*
|
||||
* @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery.
|
||||
* @returns {Object} jQuery object.
|
||||
*/
|
||||
jqLite = jQuery || jqLiteWrap,
|
||||
slice = Array.prototype.slice,
|
||||
push = Array.prototype.push,
|
||||
error = window[$console] ? bind(window[$console], window[$console]['error'] || noop) : noop,
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc overview
|
||||
* @name angular
|
||||
* @namespace The exported angular namespace.
|
||||
*/
|
||||
angular = window[$angular] || (window[$angular] = {}),
|
||||
angularTextMarkup = extensionMap(angular, 'markup'),
|
||||
angularAttrMarkup = extensionMap(angular, 'attrMarkup'),
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc overview
|
||||
* @name angular.directive
|
||||
* @namespace Namespace for all directives.
|
||||
*
|
||||
* @description
|
||||
* A directive is an HTML attribute that you can use in an existing HTML element type or in a
|
||||
* DOM element type that you create as {@link angular.widget}, to modify that element's
|
||||
* properties. You can use any number of directives per element.
|
||||
*
|
||||
* For example, you can add the ng:bind directive as an attribute of an HTML span element, as in
|
||||
* `<span ng:bind="1+2"></span>`. How does this work? The compiler passes the attribute value
|
||||
* `1+2` to the ng:bind extension, which in turn tells the {@link angular.scope} to watch that
|
||||
* expression and report changes. On any change it sets the span text to the expression value.
|
||||
*
|
||||
* Here's how to define {@link angular.directive.ng:bind ng:bind}:
|
||||
* <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>
|
||||
*
|
||||
* # Directive vs. Attribute Widget
|
||||
* Both [attribute widgets](#!angular.widget) and directives can compile a DOM element
|
||||
* attribute. So why have two different ways to do the same thing? The answer is that order
|
||||
* matters, but we have no control over the order in which attributes are read. To solve this
|
||||
* we apply attribute widget before the directive.
|
||||
*
|
||||
* For example, consider this piece of HTML, which uses the directives `ng:repeat`, `ng:init`,
|
||||
* and `ng:bind`:
|
||||
* <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. We need to execute
|
||||
* {@link angular.directive.ng:repeat ng:repeat} before we run the
|
||||
* {@link angular.directive.ng:init ng:init} and `ng:bind` on the `<li/>;`. This is because we
|
||||
* want to run the `ng:init="a=a+1` and `ng:bind="person"` once for each person in people. We
|
||||
* could not have used directive to create this template because attributes are read in an
|
||||
* unspecified order and there is no way of guaranteeing that the repeater attribute would
|
||||
* execute first. Using the `ng:repeat` attribute directive ensures that we can transform the
|
||||
* DOM element into a template.
|
||||
*
|
||||
* Widgets run before directives. Widgets may manipulate the DOM whereas directives are not
|
||||
* expected to do so, and so they run last.
|
||||
*/
|
||||
/** @name angular.directive */
|
||||
angularDirective = extensionMap(angular, 'directive'),
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc overview
|
||||
* @name angular.widget
|
||||
* @namespace Namespace for all widgets.
|
||||
* @description
|
||||
* # Overview
|
||||
* Widgets allow you to create DOM elements that the browser doesn't
|
||||
* already understand. You create the widget in your namespace and
|
||||
* assign it behavior. You can only bind one widget per DOM element
|
||||
* (unlike directives, in which you can use any number per DOM
|
||||
* element). Widgets are expected to manipulate the DOM tree by
|
||||
* adding new elements whereas directives are expected to only modify
|
||||
* element properties.
|
||||
*
|
||||
* Widgets come in two flavors: element and attribute.
|
||||
*
|
||||
* # Element Widget
|
||||
* 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>
|
||||
* <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>
|
||||
*
|
||||
* # Attribute Widget
|
||||
* Let's implement the same widget, but this time as an attribute
|
||||
* that can be added to any existing DOM element.
|
||||
* <pre>
|
||||
* <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>
|
||||
*
|
||||
* @example
|
||||
* <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>
|
||||
*/
|
||||
/** @name angular.widget */
|
||||
angularWidget = extensionMap(angular, 'widget', lowercase),
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc overview
|
||||
* @name angular.validator
|
||||
* @namespace Namespace for all filters.
|
||||
* @description
|
||||
* # Overview
|
||||
* Validators are a standard way to check the user input against a specific criteria. For
|
||||
* example, you might need to check that an input field contains a well-formed phone number.
|
||||
*
|
||||
* # Syntax
|
||||
* Attach a validator on user input widgets using the `ng:validate` attribute.
|
||||
*
|
||||
* <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>
|
||||
*
|
||||
*
|
||||
* # Writing your own Validators
|
||||
* Writing your own validator is easy. To make a function available as a
|
||||
* validator, just define the JavaScript function on the `angular.validator`
|
||||
* object. <angular/> passes in the input to validate as the first argument
|
||||
* to your function. Any additional validator arguments are passed in as
|
||||
* additional arguments to your function.
|
||||
*
|
||||
* You can use these variables in the function:
|
||||
*
|
||||
* * `this` — The current scope.
|
||||
* * `this.$element` — The DOM element containing the binding. This allows the filter to manipulate
|
||||
* the DOM in addition to transforming the input.
|
||||
*
|
||||
* In this example we have written a upsTrackingNo validator.
|
||||
* It marks the input text "valid" only when the user enters a well-formed
|
||||
* UPS tracking number.
|
||||
*
|
||||
* @css ng-validation-error
|
||||
* When validation fails, this css class is applied to the binding, making its borders red by
|
||||
* default.
|
||||
*
|
||||
* @example
|
||||
* <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"/>
|
||||
*
|
||||
* @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/);
|
||||
* });
|
||||
*
|
||||
*/
|
||||
/** @name angular.validator */
|
||||
angularValidator = extensionMap(angular, 'validator'),
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc overview
|
||||
* @name angular.filter
|
||||
* @namespace Namespace for all filters.
|
||||
* @description
|
||||
* # Overview
|
||||
* Filters are a standard way to format your data for display to the user. For example, you
|
||||
* might have the number 1234.5678 and would like to display it as US currency: $1,234.57.
|
||||
* Filters allow you to do just that. In addition to transforming the data, filters also modify
|
||||
* the DOM. This allows the filters to for example apply css styles to the filtered output if
|
||||
* certain conditions were met.
|
||||
*
|
||||
*
|
||||
* # Standard Filters
|
||||
*
|
||||
* The Angular framework provides a standard set of filters for common operations, including:
|
||||
* {@link angular.filter.currency currency}, {@link angular.filter.json json},
|
||||
* {@link angular.filter.number number}, and {@link angular.filter.html html}. You can also add
|
||||
* your own filters.
|
||||
*
|
||||
*
|
||||
* # Syntax
|
||||
*
|
||||
* Filters can be part of any {@link angular.scope} evaluation but are typically used with
|
||||
* {{bindings}}. Filters typically transform the data to a new data type, formating the data in
|
||||
* the process. Filters can be chained and take optional arguments. Here are few examples:
|
||||
*
|
||||
* * 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. To number, the argument “5” requests 5 digits
|
||||
* to the right of the decimal point.
|
||||
*
|
||||
*
|
||||
* # Writing your own Filters
|
||||
*
|
||||
* Writing your own filter is very easy: just define a JavaScript function on `angular.filter`.
|
||||
* The framework passes in the input value as the first argument to your function. Any filter
|
||||
* arguments are passed in as additional function arguments.
|
||||
*
|
||||
* You can use these variables in the function:
|
||||
*
|
||||
* * `this` — The current scope.
|
||||
* * `this.$element` — The DOM element containing the binding. This allows the filter to manipulate
|
||||
* the DOM in addition to transforming the input.
|
||||
*
|
||||
*
|
||||
* @exampleDescription
|
||||
* The following example filter reverses a text string. In addition, it conditionally makes the
|
||||
* text upper-case (to demonstrate optional arguments) and assigns color (to demonstrate DOM
|
||||
* modification).
|
||||
*
|
||||
* @example
|
||||
<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;
|
||||
}
|
||||
if (uppercase) {
|
||||
out = out.toUpperCase();
|
||||
}
|
||||
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"}}
|
||||
|
||||
*/
|
||||
/** @name angular.fileter */
|
||||
angularFilter = extensionMap(angular, 'filter'),
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc overview
|
||||
* @name angular.formatter
|
||||
* @namespace Namespace for all formats.
|
||||
* @description
|
||||
* # Overview
|
||||
* The formatters are responsible for translating user readable text in an input widget to a
|
||||
* data model stored in an application.
|
||||
*
|
||||
* # Writting your own Formatter
|
||||
* Writing your own formatter is easy. Just register a pair of JavaScript functions with
|
||||
* `angular.formatter`. One function for parsing user input text to the stored form,
|
||||
* and one for formatting the stored data to user-visible text.
|
||||
*
|
||||
* Here is an example of a "reverse" formatter: The data is stored in uppercase and in
|
||||
* reverse, while it is displayed in lower case and non-reversed. User edits are
|
||||
* automatically parsed into the internal form and data changes are automatically
|
||||
* formatted to the viewed form.
|
||||
*
|
||||
* <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>
|
||||
*
|
||||
* @example
|
||||
* <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>
|
||||
*
|
||||
* Formatted:
|
||||
* <input type="text" name="data" value="angular" ng:format="reverse"/>
|
||||
* <br/>
|
||||
*
|
||||
* Stored:
|
||||
* <input type="text" name="data"/><br/>
|
||||
* <pre>{{data}}</pre>
|
||||
*
|
||||
*
|
||||
* @scenario
|
||||
* it('should store reverse', function(){
|
||||
* expect(element('.doc-example input:first').val()).toEqual('angular');
|
||||
* expect(element('.doc-example input:last').val()).toEqual('RALUGNA');
|
||||
*
|
||||
* this.addFutureAction('change to XYZ', function($window, $document, done){
|
||||
* $document.elements('.doc-example input:last').val('XYZ').trigger('change');
|
||||
* done();
|
||||
* });
|
||||
* expect(element('.doc-example input:first').val()).toEqual('zyx');
|
||||
* });
|
||||
*/
|
||||
/** @name angular.formatter */
|
||||
angularFormatter = extensionMap(angular, 'formatter'),
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc overview
|
||||
* @name angular.service
|
||||
*
|
||||
* @description
|
||||
* # Overview
|
||||
* Services are substituable objects, which are wired together using dependency injection.
|
||||
* 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 satisfy these dependencies, so each service must explicitely define its dependencies.
|
||||
* This is done by `$inject` property.
|
||||
*
|
||||
* For now, life time of all services is the same as the life time of page.
|
||||
*
|
||||
*
|
||||
* # Built-in services
|
||||
* The Angular framework provides a standard set of services for common operations.
|
||||
* You can write your own services and rewrite these standard services as well.
|
||||
* Like other core angular variables, the built-in services always start with $.
|
||||
*
|
||||
* * `angular.service.$browser`
|
||||
* * `angular.service.$window`
|
||||
* * `angular.service.$document`
|
||||
* * `angular.service.$location`
|
||||
* * `angular.service.$log`
|
||||
* * `angular.service.$exceptionHandler`
|
||||
* * `angular.service.$hover`
|
||||
* * `angular.service.$invalidWidgets`
|
||||
* * `angular.service.$route`
|
||||
* * `angular.service.$xhr`
|
||||
* * `angular.service.$xhr.error`
|
||||
* * `angular.service.$xhr.bulk`
|
||||
* * `angular.service.$xhr.cache`
|
||||
* * `angular.service.$resource`
|
||||
* * `angular.service.$cookies`
|
||||
* * `angular.service.$cookieStore`
|
||||
*
|
||||
* # Writing your own custom services
|
||||
* Angular provides only set of basic services, so you will probably need to write your custom
|
||||
* service very soon. To do so, you need to write a factory function and register this function
|
||||
* to angular's dependency injector. This factory function must return an object - your service
|
||||
* (it is not called with new operator).
|
||||
*
|
||||
* **angular.service** has three parameters:
|
||||
*
|
||||
* - `{string} name` - Name of the service
|
||||
* - `{function()} factory` - Factory function (called just once by DI)
|
||||
* - `{Object} config` - Hash of configuration (`$inject`, `$creation`)
|
||||
*
|
||||
* If your service requires - depends on other services, you need to specify them
|
||||
* in config hash - property $inject. This property is an array of strings (service names).
|
||||
* These dependencies will be passed as parameters to the factory function by DI.
|
||||
* This approach is very useful when testing, as you can inject mocks/stubs/dummies.
|
||||
*
|
||||
* 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 in a controllers is very similar to using service in other service.
|
||||
* Again, we will use dependency injection.
|
||||
*
|
||||
* 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
|
||||
* <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>
|
||||
*/
|
||||
/** @name angular.service */
|
||||
angularService = extensionMap(angular, 'service'),
|
||||
angularCallbacks = extensionMap(angular, 'callbacks'),
|
||||
nodeName,
|
||||
|
|
@ -942,7 +368,7 @@ function isNumber(value){ return typeof value == $number;}
|
|||
* @function
|
||||
*
|
||||
* @description
|
||||
* Checks if a reference is defined.
|
||||
* Checks if value is a date.
|
||||
*
|
||||
* @param {*} value Reference to check.
|
||||
* @returns {boolean} True if `value` is a `Date`.
|
||||
|
|
|
|||
Loading…
Reference in a new issue