Developer guide documentation

This commit is contained in:
Misko Hevery 2011-01-31 11:55:44 -08:00
parent 9fd3dfe49d
commit ed768ebc53
10 changed files with 349 additions and 10 deletions

View file

@ -0,0 +1,27 @@
@workInProgress
@ngdoc overview
@name Developer Guide: Contributing
@description
# Open Source
Angular is an open source project licensed under the
{@link http://github.com/angular/angular.js/blob/master/LICENSE MIT license}. We welcome any
contributions from the community, but in order to make the contribution process manageable, we ask
you to follow the following guidelines:
# Source Code
* Discuss any major changes on our {@link http://groups.google.com/group/angular mailing list}.
For small changes and bugfixes, just go ahead and craft your patch and submit it.
* Use coding style described
{@link http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml here}.
* To submit any changes, we ask you to use {@link http://github.com}
* Create a github {@link https://github.com/signup/free account}
* Clone the {@link http://github.com/angular/angular.js main angular repository}
* Create a new branch off of the master (`git branch my-fix-branch`)
* Switch to the branch (`git checkout my-fix-branch`), make your changes and commit (`git commit -a`)
* For any new features or regressions we require a test(s) to be submitted with the change
* Run js lint (`rake lint`) and fix any warnings or errors
* Push your branch to github (`git push origin my-fix-branch`)
* Send a pull request via github into `angular:master`
* Once the patch is reviewed and merged, you can delete your branch (`git push origin :my-fix-branch; git checkout master; git branch -D my-fix-branch`)
* TADA!

46
docs/guide.css.ngdoc Normal file
View file

@ -0,0 +1,46 @@
@workInProgress
@ngdoc overview
@name Developer Guide: CSS
@description
# CSS
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 angualr.expression expression} 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 angular.widget.@ng:validate ng:validate example}.
## How to override the styles for built-in classes
To override the styles for these built-in 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>

View file

@ -0,0 +1,35 @@
@workInProgress
@ngdoc overview
@name Developer Guide: Downloading
@description
# Downloading
Follow these steps to download, compile, and host angular.js on your own server:
## Step 0
If you don't already have Rake (Ruby Make) installed, download and install it from
http://rake.rubyforge.org/.
## Step 1
Download the angular source code from GitHub: http://github.com/angular/angular.js.
## Step 2
The source code should include a Rakefile. Compile the source by typing rake compile.
## Step 3
Host the files on your server. Look through your source tree and make sure that the css and js
directories are parallel (i.e. share a common root node).
In the js directory you should see these different versions of the angular bootstrap code:
* **angular-?.?.?.js** - this file is unobfuscated, uncompressed, and human-readable. Note that
despite the name of the file, there is no additional functionality built in to help you debug
your application; it has the prefix debug because you can read the source code.
* **angular-?.?.?.min.js** - this is a compressed and obfuscated version of `angular-?.?.?.js`.
You might want to use this version if you want to load a smaller but functionally equivalent
version of the code in your application. Note: this minified version was created using the
Closure Compiler.

207
docs/guide.expression.ngdoc Normal file
View file

@ -0,0 +1,207 @@
@workInProgress
@ngdoc overview
@name Developer Guide: Expression
@description
# Expressions
Expressions are the bindings that you write in HTML and embed in templates in order to create
views in angular. They are 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 you want to run arbitrary JavaScript code, 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 are 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. E.g. 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 (e.g. 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 $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:
<pre>
name | uppercase
</pre>
The expression evaluator simply passes the value of name to angular.filter.uppercase.
Chain filters using this syntax:
<pre>
value | filter1 | filter2
</pre>
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 chooses 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 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.

View file

@ -34,3 +34,4 @@
* service:$browser(mock)
* {@link guide.downloading Downloading} - How to download, compile, and host the angular
environment on your own server.
* {@link guide.contribute Contributing} - How to contribute to angular project.

8
docs/guide.testing.ngdoc Normal file
View file

@ -0,0 +1,8 @@
@workInProgress
@ngdoc overview
@name Developer Guide: Testing
@description
# Testing Angular Applications
to be written...

View file

@ -319,6 +319,7 @@ describe('ngdoc', function(){
var doc = new Doc("@description " +
'foo {@link angular.foo}\n\n da {@link angular.foo bar foo bar } \n\n' +
'dad{@link angular.foo}\n\n' +
'external{@link http://angularjs.org}\n\n' +
'{@link angular.directive.ng:foo ng:foo}');
doc.parse();
expect(doc.description).
@ -329,6 +330,8 @@ describe('ngdoc', function(){
toContain('dad<a href="#!angular.foo"><code>angular.foo</code></a>');
expect(doc.description).
toContain('<a href="#!angular.directive.ng:foo"><code>ng:foo</code></a>');
expect(doc.description).
toContain('<a href="http://angularjs.org">http://angularjs.org</a>');
});
});
@ -357,12 +360,16 @@ describe('ngdoc', function(){
});
it('should render description in related method', function(){
var doc = new Doc();
var doc = new Doc('').parse();
doc.ngdoc = 'service';
doc.methods = [new Doc('@ngdoc method\n@exampleDescription MDesc\n@example MExmp').parse()];
doc.properties = [new Doc('@ngdoc property\n@exampleDescription PDesc\n@example PExmp').parse()];
expect(doc.html()).toContain('<p>MDesc</p><div ng:non-bindable=""><pre class="brush: js; html-script: true;">MExmp</pre>');
expect(doc.html()).toContain('<p>PDesc</p><div ng:non-bindable=""><pre class="brush: js; html-script: true;">PExmp</pre>');
doc.methods = [
new Doc('@ngdoc method\n@exampleDescription MDesc\n@example MExmp').parse()];
doc.properties = [
new Doc('@ngdoc property\n@exampleDescription PDesc\n@example PExmp').parse()];
expect(doc.html()).toContain('<p>MDesc</p><div ng:non-bindable="">' +
'<pre class="brush: js; html-script: true;">MExmp</pre>');
expect(doc.html()).toContain('<p>PDesc</p><div ng:non-bindable="">' +
'<pre class="brush: js; html-script: true;">PExmp</pre>');
});
});

View file

@ -114,7 +114,7 @@ Doc.prototype = {
description: markdown(text.replace(match[0], match[2]))
};
} else if(atName == 'description') {
text.replace(/<doc:scenario>([\s\S]*)<\/doc:scenario>/mi,
text.replace(/<doc:scenario>([\s\S]*?)<\/doc:scenario>/gmi,
function(_, scenario){
self.scenarios.push(scenario);
});
@ -154,7 +154,7 @@ Doc.prototype = {
throw new Error("Don't know how to format @ngdoc: " + self.ngdoc);
}).call(self, dom);
dom.example(self.exampleDescription, self.example, self.scenario);
dom.example(self.exampleDescription, self.example, self.scenarios[0]);
});
return dom.toString();
@ -438,6 +438,8 @@ Doc.prototype = {
//////////////////////////////////////////////////////////
function markdown (text) {
var IS_URL = /^(https?:\/\/|ftps?:\/\/|mailto:)/;
var IS_ANGULAR = /^angular\./;
if (!text) return text;
var parts = text.split(/(<pre>[\s\S]*?<\/pre>|<doc:example>[\s\S]*?<\/doc:example>)/),
match;
@ -462,10 +464,10 @@ function markdown (text) {
text = text.replace(/<angular\/>/gm, '<tt>&lt;angular/&gt;</tt>');
text = text.replace(/{@link ([^\s}]+)((\s|\n)+(.+?))?\s*}/gm,
function(_all, url, _2, _3, title){
return '<a href="#!' + url + '">'
+ (url.match(/^angular\./) ? '<code>' : '')
return '<a href="' + (url.match(IS_URL) ? '' : '#!') + url + '">'
+ (url.match(IS_ANGULAR) ? '<code>' : '')
+ (title || url)
+ (url.match(/^angular\./) ? '</code>' : '')
+ (url.match(IS_ANGULAR) ? '</code>' : '')
+ '</a>';
});
text = new Showdown.converter().makeHtml(text);

View file

@ -56,6 +56,7 @@
});
function indent(text) {
if (!text) return text;
var lines = text.split(/\n/);
var lineNo = [];
// remove any leading blank lines

View file

@ -40,4 +40,9 @@ function DocsController($location, $browser, $window) {
}
// prevent compilation of code
angular.widget('code', function(element){
element.attr('ng:non-bindable', 'true');
});
SyntaxHighlighter['defaults'].toolbar = false;