From 11e9572b952e49b01035e956c412d6095533031a Mon Sep 17 00:00:00 2001 From: Misko Hevery Date: Fri, 29 Apr 2011 15:18:27 -0700 Subject: Move documentation under individual headings --- docs/content/api/angular.attrMarkup.ngdoc | 15 ++ docs/content/api/angular.directive.ngdoc | 53 +++++ docs/content/api/angular.element.ngdoc | 49 +++++ docs/content/api/angular.filter.ngdoc | 87 ++++++++ docs/content/api/angular.formatter.ngdoc | 82 ++++++++ docs/content/api/angular.markup.ngdoc | 66 ++++++ docs/content/api/angular.ngdoc | 4 + docs/content/api/angular.service.ngdoc | 175 ++++++++++++++++ docs/content/api/angular.validator.ngdoc | 77 +++++++ docs/content/api/angular.widget.ngdoc | 78 +++++++ docs/content/cookbook/buzz.ngdoc | 63 ++++++ docs/content/cookbook/deeplinking.ngdoc | 114 ++++++++++ docs/content/cookbook/form.ngdoc | 103 +++++++++ docs/content/cookbook/formadvanced.ngdoc | 105 ++++++++++ docs/content/cookbook/helloworld.ngdoc | 31 +++ docs/content/cookbook/index.ngdoc | 60 ++++++ docs/content/cookbook/mvc.ngdoc | 125 +++++++++++ docs/content/guide/bootstrap.ngdoc | 97 +++++++++ docs/content/guide/data-binding.ngdoc | 41 ++++ docs/content/guide/expression.ngdoc | 207 ++++++++++++++++++ docs/content/guide/guide.compiler.ngdoc | 163 +++++++++++++++ docs/content/guide/guide.css.ngdoc | 46 ++++ docs/content/guide/guide.di.ngdoc | 304 +++++++++++++++++++++++++++ docs/content/guide/index.ngdoc | 37 ++++ docs/content/guide/overview.ngdoc | 337 ++++++++++++++++++++++++++++++ docs/content/guide/template.ngdoc | 22 ++ docs/content/guide/testing.ngdoc | 8 + docs/content/intro/contribute.ngdoc | 233 +++++++++++++++++++++ docs/content/intro/downloading.ngdoc | 70 +++++++ docs/content/intro/faq.ngdoc | 81 +++++++ docs/content/intro/started.ngdoc | 146 +++++++++++++ docs/content/intro/testimonials.ngdoc | 33 +++ docs/content/tutorial/index.ngdoc | 172 +++++++++++++++ docs/content/tutorial/step_00.ngdoc | 77 +++++++ docs/content/tutorial/step_01.ngdoc | 88 ++++++++ docs/content/tutorial/step_02.ngdoc | 137 ++++++++++++ docs/content/tutorial/step_03.ngdoc | 108 ++++++++++ docs/content/tutorial/step_04.ngdoc | 161 ++++++++++++++ docs/content/tutorial/step_05.ngdoc | 147 +++++++++++++ docs/content/tutorial/step_06.ngdoc | 113 ++++++++++ docs/content/tutorial/step_07.ngdoc | 181 ++++++++++++++++ docs/content/tutorial/step_08.ngdoc | 148 +++++++++++++ docs/content/tutorial/step_09.ngdoc | 108 ++++++++++ docs/content/tutorial/step_10.ngdoc | 110 ++++++++++ docs/content/tutorial/step_11.ngdoc | 178 ++++++++++++++++ 45 files changed, 4840 insertions(+) create mode 100644 docs/content/api/angular.attrMarkup.ngdoc create mode 100644 docs/content/api/angular.directive.ngdoc create mode 100644 docs/content/api/angular.element.ngdoc create mode 100644 docs/content/api/angular.filter.ngdoc create mode 100644 docs/content/api/angular.formatter.ngdoc create mode 100644 docs/content/api/angular.markup.ngdoc create mode 100644 docs/content/api/angular.ngdoc create mode 100644 docs/content/api/angular.service.ngdoc create mode 100644 docs/content/api/angular.validator.ngdoc create mode 100644 docs/content/api/angular.widget.ngdoc create mode 100644 docs/content/cookbook/buzz.ngdoc create mode 100644 docs/content/cookbook/deeplinking.ngdoc create mode 100644 docs/content/cookbook/form.ngdoc create mode 100644 docs/content/cookbook/formadvanced.ngdoc create mode 100644 docs/content/cookbook/helloworld.ngdoc create mode 100644 docs/content/cookbook/index.ngdoc create mode 100644 docs/content/cookbook/mvc.ngdoc create mode 100644 docs/content/guide/bootstrap.ngdoc create mode 100644 docs/content/guide/data-binding.ngdoc create mode 100644 docs/content/guide/expression.ngdoc create mode 100644 docs/content/guide/guide.compiler.ngdoc create mode 100644 docs/content/guide/guide.css.ngdoc create mode 100644 docs/content/guide/guide.di.ngdoc create mode 100644 docs/content/guide/index.ngdoc create mode 100644 docs/content/guide/overview.ngdoc create mode 100644 docs/content/guide/template.ngdoc create mode 100644 docs/content/guide/testing.ngdoc create mode 100644 docs/content/intro/contribute.ngdoc create mode 100644 docs/content/intro/downloading.ngdoc create mode 100644 docs/content/intro/faq.ngdoc create mode 100644 docs/content/intro/started.ngdoc create mode 100644 docs/content/intro/testimonials.ngdoc create mode 100644 docs/content/tutorial/index.ngdoc create mode 100755 docs/content/tutorial/step_00.ngdoc create mode 100755 docs/content/tutorial/step_01.ngdoc create mode 100755 docs/content/tutorial/step_02.ngdoc create mode 100755 docs/content/tutorial/step_03.ngdoc create mode 100755 docs/content/tutorial/step_04.ngdoc create mode 100755 docs/content/tutorial/step_05.ngdoc create mode 100755 docs/content/tutorial/step_06.ngdoc create mode 100755 docs/content/tutorial/step_07.ngdoc create mode 100755 docs/content/tutorial/step_08.ngdoc create mode 100755 docs/content/tutorial/step_09.ngdoc create mode 100644 docs/content/tutorial/step_10.ngdoc create mode 100644 docs/content/tutorial/step_11.ngdoc (limited to 'docs/content') diff --git a/docs/content/api/angular.attrMarkup.ngdoc b/docs/content/api/angular.attrMarkup.ngdoc new file mode 100644 index 00000000..aad20866 --- /dev/null +++ b/docs/content/api/angular.attrMarkup.ngdoc @@ -0,0 +1,15 @@ +@workInProgress +@ngdoc overview +@name angular.attrMarkup + +@description +Attribute markup extends the angular compiler in a very similar way as {@link angular.markup} except +that it allows you to modify the state of the attribute text rather then the content of a node. + +
+angular.attrMarkup('extraClass', function(attrValue, attrName, element){
+  if (attrName == 'additional-class') {
+    element.addClass(attrValue);
+  }
+});
+
diff --git a/docs/content/api/angular.directive.ngdoc b/docs/content/api/angular.directive.ngdoc new file mode 100644 index 00000000..9a08e4c7 --- /dev/null +++ b/docs/content/api/angular.directive.ngdoc @@ -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 +``. 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}: +
+  angular.directive('ng:bind', function(expression, compiledElement) {
+    var compiler = this;
+    return function(linkElement) {
+      var currentScope = this;
+      currentScope.$watch(expression, function(value) {
+        linkElement.text(value);
+      });
+    };
+  });
+
+ +# 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`: +
+  
+
+ +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 `
  • ;`. 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. diff --git a/docs/content/api/angular.element.ngdoc b/docs/content/api/angular.element.ngdoc new file mode 100644 index 00000000..2ce007fd --- /dev/null +++ b/docs/content/api/angular.element.ngdoc @@ -0,0 +1,49 @@ +@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 (as long as it was loaded before `DOMContentEvent`) + +Angular's jQuery lite implementation is a tiny API-compatible subset of jQuery which allows +angular to manipulate DOM. The jQuery lite implements only a subset of jQuery api, with the +focus on the most commonly needed functionality and minimal footprint. For this reason only a +limited number of jQuery methods, arguments and invocation styles are supported. + +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/) + +## Additionally these methods extend the jQuery and are available in both jQuery and jQuery lite +version: + +- `scope()` - retrieves the current angular scope of the element. + +@param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery. +@returns {Object} jQuery object. diff --git a/docs/content/api/angular.filter.ngdoc b/docs/content/api/angular.filter.ngdoc new file mode 100644 index 00000000..5d4f5940 --- /dev/null +++ b/docs/content/api/angular.filter.ngdoc @@ -0,0 +1,87 @@ +@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. + + +@example + 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). + + + + + +
    + No filter: {{text}}
    + Reverse: {{text|reverse}}
    + Reverse + uppercase: {{text|reverse:true}}
    + Reverse + uppercase + blue: {{text|reverse:true:"blue"}} +
    + + it('should reverse text', function(){ + expect(binding('text|reverse')).toEqual('olleh'); + input('text').enter('ABC'); + expect(binding('text|reverse')).toEqual('CBA'); + }); + +
    + + diff --git a/docs/content/api/angular.formatter.ngdoc b/docs/content/api/angular.formatter.ngdoc new file mode 100644 index 00000000..4eef190e --- /dev/null +++ b/docs/content/api/angular.formatter.ngdoc @@ -0,0 +1,82 @@ +@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. + +
    +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();
    +  }
    +});
    +
    + +@example + + + + + Formatted: + +
    + + Stored: +
    +
    {{data}}
    +
    + + it('should store reverse', function(){ + expect(element('.doc-example-live input:first').val()).toEqual('angular'); + expect(element('.doc-example-live input:last').val()).toEqual('RALUGNA'); + + this.addFutureAction('change to XYZ', function($window, $document, done){ + $document.elements('.doc-example-live input:last').val('XYZ').trigger('change'); + done(); + }); + expect(element('.doc-example-live input:first').val()).toEqual('zyx'); + }); + +
    + diff --git a/docs/content/api/angular.markup.ngdoc b/docs/content/api/angular.markup.ngdoc new file mode 100644 index 00000000..a40385c8 --- /dev/null +++ b/docs/content/api/angular.markup.ngdoc @@ -0,0 +1,66 @@ +@workInProgress +@ngdoc overview +@name angular.markup + +@description +#Overview +Markups allow the angular compiler to transform content of DOM elements or portions of this content +into other text or DOM elements for further compilation. Markup extensions do not themselves produce +linking functions. Think of markup as a way to produce shorthand for a {@link angular.widget widget} + or a {@link angular.directive directive}. + +#`{{}}` (double curly) built-in markup +`{{}}` markup is a built-in markup, which translates the enclosed expression into an +{@link angular.directive.ng:bind ng:bind} directive. It simply transforms + +
    +{{expression}}
    +
    + +to: + +
    +
    +
    + +For example `{{1+2}}` is easier to write and understand than ``. The +expanded elements are then {@link guide.compiler compiled} normally. + +# Custom markup +Let's say you want to define this shorthand for a horizontal rule: `---` for `
    `. + +In other words, this HTML: +
    +header
    +---
    +footer
    +
    + +should translate to: +
    +header
    +
    +footer +
    + +Here's how the angular compiler could be extended to achieve this: +
    +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();
    +  }
    +});
    +
    + +Unlike {@link angular.widget widgets} and {@link angular.directive directives}, in which the +compiler matches the name of handler function to a DOM element or attribute name, for markup 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. diff --git a/docs/content/api/angular.ngdoc b/docs/content/api/angular.ngdoc new file mode 100644 index 00000000..3f342d1b --- /dev/null +++ b/docs/content/api/angular.ngdoc @@ -0,0 +1,4 @@ +@workInProgress +@ngdoc overview +@name angular +@namespace The exported angular namespace. diff --git a/docs/content/api/angular.service.ngdoc b/docs/content/api/angular.service.ngdoc new file mode 100644 index 00000000..0d3406e5 --- /dev/null +++ b/docs/content/api/angular.service.ngdoc @@ -0,0 +1,175 @@ +@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. + +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} + +# 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.} - 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. +
    +       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']});
    +
    + +And here is a unit test for this service. We use Jasmine spy (mock) instead of real browser's alert. +
    +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"]);
    +});
    +
    + +# 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. +
    +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'];
    +
    + +@example + + + + +
    +

    Let's try this simple notify service, injected into the controller...

    + + +
    +
    + + it('should test service', function(){ + expect(element(':input[name=message]').val()).toEqual('test'); + }); + +
    diff --git a/docs/content/api/angular.validator.ngdoc b/docs/content/api/angular.validator.ngdoc new file mode 100644 index 00000000..96b1e76a --- /dev/null +++ b/docs/content/api/angular.validator.ngdoc @@ -0,0 +1,77 @@ +@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. + + + + Change me: + + + 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/); + }); + + + + +# 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. 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 + + + + + + + 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/); + }); + + diff --git a/docs/content/api/angular.widget.ngdoc b/docs/content/api/angular.widget.ngdoc new file mode 100644 index 00000000..5fd7b259 --- /dev/null +++ b/docs/content/api/angular.widget.ngdoc @@ -0,0 +1,78 @@ +@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. + +
    +<my:watch exp="name"/>
    +
    + +You can implement `my:watch` like this: +
    +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);
    +    });
    +  };
    +});
    +
    + +# Attribute Widget +Let's implement the same widget, but this time as an attribute +that can be added to any existing DOM element. +
    +<div my:watch="name">text</div>
    +
    +You can implement `my:watch` attribute like this: +
    +angular.widget('@my:watch', function(expression, compileElement) {
    +  var compiler = this;
    +  return function(linkElement) {
    +    var currentScope = this;
    +    currentScope.$watch(expression, function(value){
    +      alert(value);
    +    });
    +  };
    +});
    +
    + +@example + + + + + + + + diff --git a/docs/content/cookbook/buzz.ngdoc b/docs/content/cookbook/buzz.ngdoc new file mode 100644 index 00000000..2e82b2d1 --- /dev/null +++ b/docs/content/cookbook/buzz.ngdoc @@ -0,0 +1,63 @@ +@workInProgress +@ngdoc overview +@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} +to retrieve Buzz activity and comments. + + + + +
    + + +
    +
    +

    + + {{item.actor.name}} + + Expand replies: {{item.links.replies[0].count}} + +

    + {{item.object.content | html}} +
    + + {{reply.actor.name}}: + {{reply.content | html}} +
    +
    +
    +
    + + it('fetch buzz and expand', function(){ + element(':button:contains(fetch)').click(); + expect(repeater('div.buzz').count()).toBeGreaterThan(0); + element('.buzz a:contains(Expand replies):first').click(); + expect(repeater('div.reply').count()).toBeGreaterThan(0); + }); + +
    diff --git a/docs/content/cookbook/deeplinking.ngdoc b/docs/content/cookbook/deeplinking.ngdoc new file mode 100644 index 00000000..7d69ee84 --- /dev/null +++ b/docs/content/cookbook/deeplinking.ngdoc @@ -0,0 +1,114 @@ +@workInProgress +@ngdoc overview +@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 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, +the home screen, settings screen, details screen, etc. For each of these screens, we would like to +assign a URL so that it can be bookmarked and later restored. Each of these screens will be +associated with a controller which define the screen's behavior. The most common case is that the +screen will be constructed from an HTML snippet, which we will refer to as the partial. Screens can +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} + + + + + +
    +

    Your App Chrome

    + [ Welcome | Settings ] +
    + + Partial: {{$route.current.template}} + + + Your app footer +
    +
    + + it('should navigate to URL', function(){ + element('a:contains(Welcome)').click(); + expect(element('ng\\:view').text()).toMatch(/Hello anonymous/); + element('a:contains(Settings)').click(); + input('form.name').enter('yourname'); + element(':button:contains(Save)').click(); + element('a:contains(Welcome)').click(); + expect(element('ng\\:view').text()).toMatch(/Hello yourname/); + }); + +
    + + + +# 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 + appropriate controller when the URL changes. +* The {@link 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. diff --git a/docs/content/cookbook/form.ngdoc b/docs/content/cookbook/form.ngdoc new file mode 100644 index 00000000..c9fd9e9a --- /dev/null +++ b/docs/content/cookbook/form.ngdoc @@ -0,0 +1,103 @@ +@workInProgress +@ngdoc overview +@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. + + + + + +
    + +
    +

    + +
    +
    + , + +

    + + + [ add ] +
    + + + [ X ] +
    +
    + Debug View: +
    user={{user}}
    +
    + +
    + + it('should show debug', function(){ + expect(binding('user')).toMatch(/John Smith/); + }); + it('should add contact', function(){ + using('.example').element('a:contains(add)').click(); + using('.example div:last').input('contact.value').enter('you@example.org'); + expect(binding('user')).toMatch(/\(234\) 555\-1212/); + 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/); + + using('.example').input('user.address.zip').enter('abc'); + + expect(using('.example').element(':input[name=user.address.zip]').attr('className')) + .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/); + + using('.example').input('user.address.state').enter('XXX'); + + expect(using('.example').element(':input[name=user.address.state]').attr('className')) + .toMatch(/ng-validation-error/); + }); + +
    + + +# Things to notice + +* The user data model is initialized {@link angular.ng:controller controller} and is available in + the {@link 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 + 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 + reflected in the view. diff --git a/docs/content/cookbook/formadvanced.ngdoc b/docs/content/cookbook/formadvanced.ngdoc new file mode 100644 index 00000000..181dd5e9 --- /dev/null +++ b/docs/content/cookbook/formadvanced.ngdoc @@ -0,0 +1,105 @@ +@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. + + + + +
    + +
    +

    + +
    +
    + , + +

    + + + [ add ] +
    + + + [ X ] +
    + + + +
    + Debug View: +
    form={{form}}
    +    master={{master}}
    +
    +
    + + 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'); + }); + +
    + + +#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. diff --git a/docs/content/cookbook/helloworld.ngdoc b/docs/content/cookbook/helloworld.ngdoc new file mode 100644 index 00000000..ab4c337a --- /dev/null +++ b/docs/content/cookbook/helloworld.ngdoc @@ -0,0 +1,31 @@ +@workInProgress +@ngdoc overview +@name Cookbook: Hello World +@description + + + + Your name: +
    + Hello {{name}}! +
    + + it('should change the binding when user enters text', function(){ + expect(binding('name')).toEqual('World'); + input('name').enter('angular'); + expect(binding('name')).toEqual('angular'); + }); + +
    + +# 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. +* 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 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 + input field in the greeting text. diff --git a/docs/content/cookbook/index.ngdoc b/docs/content/cookbook/index.ngdoc new file mode 100644 index 00000000..7dc937c5 --- /dev/null +++ b/docs/content/cookbook/index.ngdoc @@ -0,0 +1,60 @@ +@workInProgress +@ngdoc overview +@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 +classic Hello World! + + +# Basic Form + +{@link cookbook.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 +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 +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 +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 +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 +services to get and update data. Resources are the abstractions of external URLs which are +specially tailored to angular data binding. + diff --git a/docs/content/cookbook/mvc.ngdoc b/docs/content/cookbook/mvc.ngdoc new file mode 100644 index 00000000..94688547 --- /dev/null +++ b/docs/content/cookbook/mvc.ngdoc @@ -0,0 +1,125 @@ +@workInProgress +@ngdoc overview +@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. + + + + + +

    Tic-Tac-Toe

    +
    + Next Player: {{nextMove}} +
    Player {{winner}} has won!
    + + + + +
    {{cell}}
    + +
    +
    + + it('should play a game', function(){ + piece(1, 1); + expect(binding('nextMove')).toEqual('O'); + piece(3, 1); + expect(binding('nextMove')).toEqual('X'); + piece(1, 2); + piece(3, 2); + piece(1, 3); + expect(element('.winner').text()).toEqual('Player X has won!'); + }); + + function piece(row, col) { + element('.board tr:nth-child('+row+') td:nth-child('+col+')').click(); + } + +
    + + +# Things to notice + +* The controller is defined in JavaScript and has no reference to the rendering logic. +* The controller is instantiated by and injected into the view. +* The controller can be instantiated in isolation (without a view) and the code will still execute. + This makes it very testable. +* The HTML view is a projection of the model. In the above example, the model is stored in the + board variable. +* All of the controller's properties (such as board and nextMove) are available to the view. +* Changing the model changes 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. diff --git a/docs/content/guide/bootstrap.ngdoc b/docs/content/guide/bootstrap.ngdoc new file mode 100644 index 00000000..12028796 --- /dev/null +++ b/docs/content/guide/bootstrap.ngdoc @@ -0,0 +1,97 @@ +@workInProgress +@ngdoc overview +@name Developer Guide: Bootstrap +@description + +# Bootstrap +This section explains how to bootstrap your application to the angular environment using either +the `angular.js` or `angular.min.js` script. + +## The bootstrap code + +Note that there are two versions of the bootstrap code that you can use: + +* `angular-0.0.0.js` - this file is unobfuscated, uncompressed, and thus human-readable. +* `angular-0.0.0.min.js` - this is a compressed and obfuscated version of angular-debug.js. + +In this section and throughout the Developer Guide, feel free to use `angular.min.js` instead of +`angular.js` when working through code examples. + +## ng:autobind + +The simplest way to get an angular application up and running is by inserting a script tag in your +HTML file that bootstraps the `angular.js` code and uses the special `ng:autobind` attribute, +like in this snippet of HTML: + + + + Hello {{'World'}}! + + + +The `ng:autobind` attribute tells angular to compile and manage the whole HTML document. The +compilation occurs in the page's onLoad handler. Note that you don't need to explicitly add an +onLoad event; auto bind mode takes care of all the magic for you. + +## Manual bind + +Using autobind mode is a handy way to start using angular, but advanced users who want more +control over the initialization process might prefer to use manual bind mode instead. + +The best way to get started with manual bind mode is to look at the magic behind `ng:autobind` +by writing out each step of the autobind process explicitly. Note that the following code is +equivalent to the code in the previous section. + +
    +
    +
    + 
    + 
    + 
    +  Hello {{'World'}}!
    + 
    +
    +
    + +This is the sequence that your code should follow if you're writing your own manual binding code: + + * After the page is loaded, find the root of the HTML template, which is typically the root of + the document. + * Run the HTML compiler, which converts the templates into an executable, bi-directionally + bound application. + + +# XML Namespace + +**IMPORTANT:** When using angular you must declare the `ng` namespace using the `xmlns` tag. + If you don't declare the namespace, Internet Explorer does not render widgets properly. + +
    +
    +
    + + +# Create your own namespace + +If you want to define your own widgets, you must create your own namespace and use that namespace +to form the fully qualified widget name. 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: + +
    +
    +
    + + +# Global Object + +The angular script creates a single global variable `angular` in the global namespace. All APIs are +bound to fields of this global object. + diff --git a/docs/content/guide/data-binding.ngdoc b/docs/content/guide/data-binding.ngdoc new file mode 100644 index 00000000..12a926bd --- /dev/null +++ b/docs/content/guide/data-binding.ngdoc @@ -0,0 +1,41 @@ +@workInProgress +@ngdoc overview +@name Developer Guide: Data Binding +@description + +# Data Binding + +Data-binding allows you to treat the model as the single-source-of-truth of your application, and +consider the view as only a projection of the model, at all times. The process of copying the model +values to the view, and any changes to the view by the user to the model, is known as data-binding. + +## Classical Template Systems + + +At the highest level, angular looks 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: two-way data binding. + +Most templating systems bind data in only one direction: they merge a template and model together +into a view, as illustrated in the diagram to the right. After the merge occurs, any changes to +the model or in 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. + + +# angular Template Systems + +The way angular templates works is different, as illustrated in the diagram on the right. 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. + +For details about how data binding works in angular, see {@link angular.scope Scope}. diff --git a/docs/content/guide/expression.ngdoc b/docs/content/guide/expression.ngdoc new file mode 100644 index 00000000..421dd9c7 --- /dev/null +++ b/docs/content/guide/expression.ngdoc @@ -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 + + + 1+2={{1+2}} + + + it('should calculate expression in binding', function(){ + expect(binding('1+2')).toEqual('3'); + }); + + + +You can try evaluating different expressions here: + + + +
    + Expression: + + +
      +
    • + [ X ] + {{expr}} => +
    • +
    +
    +
    + + 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"]); + }); + +
    + +# 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). + + + +
    + Name: + +
    +
    + + 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'); + }); + +
    + +## 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. + + + +
    + Search: + + + + + + +
    NamePhone
    {{friend.name}}{{friend.phone}}
    +
    + + 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); + + }); + +
    + +## 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 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. + + diff --git a/docs/content/guide/guide.compiler.ngdoc b/docs/content/guide/guide.compiler.ngdoc new file mode 100644 index 00000000..8896db43 --- /dev/null +++ b/docs/content/guide/guide.compiler.ngdoc @@ -0,0 +1,163 @@ +@workInProgress +@ngdoc overview +@name Developer Guide: Compiler +@description + +#Compiler + +While angular might look like just a cool way to build web applications, the core of angular is +actually an HTML compiler. The default HTML transformations that this compiler provides are useful +for building generic apps, but you can also use them to create a domain-specific language for +building specific types of web applications. + +The compiler allows you to add behavior to existing HTML through widgets, directives, and text +markup. + +All of this compilation happens in the web browser, meaning no server is involved. + +# The compilation process +This section describes the steps that angular's HTML compiler goes through. If you use +`ng:autobind` in your application, this compilation process happens automatically when the +application is initialized (e.g. when the user loads the app in a browser). If you're an advanced +user using manual bind mode, you can decide when and how often the compilation happens. + +First, a bit of background of what the compilation step is for. Every type of +{@link angular.widget widget}, {@link angular.markup markup}, and +{@link angular.directive directive} in angular is defined with a compile function, and that +compile function returns an optional link function. Here is the relationship between the two: + + * **compile function** - registers a listener for the widget, markup, or directive's expression. + This function is called exactly once. + * **link function** - sets up the listener. This function can be called multiple times, once per + cloned DOM element (e.g. repeating element). + +Note that angular's built-in widgets, markup, and directives have predefined compile and link +functions that you don't need to modify. However, if you're writing your own widgets, markup, or +directives, you write compile and link functions. Refer to the Compiler API for more information. + +When the HTML compiler compiles a page, it goes through 3 phases: Compile, Create Root Scope, and +Link. + +## 1. Compile Phase + + * Recursively traverse the DOM, depth-first. + * Look for a matching compile function of type widget, then markup, then directive. + * If a compile function is found then execute it. + * When the compile function completes, it should return a link function. Aggregate this link + function with all link functions returned previously by step 1c. + * Repeat steps 1c and 1d for all compile functions found. The result of the compilation step is + the aggregate link function, which comprises all of the individual link functions. + +## 2. Create Root Scope + + * Inject all of the services into the root scope. + +## 3. Link Phase + + * Execute the aggregate link function with the root scope. The aggregate link function calls all + the individual link functions that were generated in the compile phase. + * 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: once for each iteration in a repeater. + +# Example + +The compilation process is best understood through example. Let's say that in your namespace my, +you want to create a new DOM element , which should display a greeting. + +If we want this HTML source: + +
    +
    + +
    +
    + +To produce this DOM: + +
    +
    + + Hello + World! + +
    +
    + +Write this widget definition (assuming you've already declared the my namespace in the page): + + +
    +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('');
    +    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);
    +    });
    +  };
    +});
    +
    + +Note: For more about widgets, see {@link angular.widget Widget}. + +## Compilation process for this example + +Here are the steps that the compiler goes through for the page that contains this widget definition: + +### Compile Phase + + * Recursively traverse the DOM depth-first. + * Find the angular.widget definition. + * Find and execute the widget's compileElement function, which includes the following steps: + * 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)). + * Extract the salutation and name HTML attributes as angular expressions. + * Return the aggregate link function, which includes just one link function in this example. + +### Link Phase + + * Execute the aggregate link function, which includes the following steps: + * Create a element set to the salutation class + * Create a element set to the name class. + * Add the span elements to the linkElement. (Note: be careful not to add them to the + compileElement, because that's the template.) + * Set up watches on the expressions. When an expression changes, copy the data to the + corresponding spans. + + +## Compiler API + +If you define your own widgets, markup, or directives, you need to access the compiler API. +This section describes the methods on the compiler that you can call. + +Note: As of 12 August 2010, these methods are subject to change. + +Recall that the compile function's this is a reference to the compiler. + + * `compile(element)` - returns `linker` - Invoke new instance of compiler to compile a DOM element + and return a linker function. You can apply the linker function to the original element or a + clone of the original element. The linker function returns a scope. + * `comment(commentText)` - returns `element` - Create a comment element. + * `element(elementName)` - returns `element` - Create an element by name. + * `text(text)` - returns `element` - Create a text element. + * `descend([set])` - returns `descend` - State Get or set the current descend state. If true the + compiler will descend to children elements. + * `directives([set])` - returns `directive` - State Get or set the current directives processing + state. The compiler will process directives only when directives set to true. + diff --git a/docs/content/guide/guide.css.ngdoc b/docs/content/guide/guide.css.ngdoc new file mode 100644 index 00000000..6e028f30 --- /dev/null +++ b/docs/content/guide/guide.css.ngdoc @@ -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: + + >
    Error message
    + + + +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: + +
    +
    +
    diff --git a/docs/content/guide/guide.di.ngdoc b/docs/content/guide/guide.di.ngdoc new file mode 100644 index 00000000..2d1f92eb --- /dev/null +++ b/docs/content/guide/guide.di.ngdoc @@ -0,0 +1,304 @@ +@workInProgress +@ngdoc overview +@name Developer Guide: Dependency Injection +@description +Dependency injection (DI) is one of the core design patterns in angular and angular applications. DI +allows you to replace almost any part of angular framework or angular application with a custom +implementation, allowing for a highly flexible, maintainable and testable code-base. + +Dependency injection is a very common pattern in Java and other statically typed languages. While +undervalued among JavaScript developers, we feel strongly that DI in JavaScript allows us to achieve +the same benefits as in other languages. + +This document will focus on using dependency injection in angular. It is outside of the scope of +this document to explain details of dependency injection. For more information on this topic, please +refer to these links: + + * {@link http://en.wikipedia.org/wiki/Dependency_injection DI - Wikipedia} + * {@link http://martinfowler.com/articles/injection.html Inversion of Control by Martin Fowler} + * Java + * {@link http://code.google.com/p/google-guice/ Guice} + * {@link http://www.devshed.com/c/a/Java/The-Spring-Framework-Understanding-IoC/ Spring} + * {@link http://picocontainer.org/injection.html picoContainer} + * .NET + * {@link http://msdn.microsoft.com/en-us/magazine/cc163739.aspx MSDN Design Patterns - Dependency Inject} + * {@link http://www.springframework.net/ Spring.NET} + + + +# Dependency Injection in angular + +Angular's dependency injection story begins with a `service`. Service in angular lingo is a +JavaScript object, function, or value that is created by angular's injector via a provided factory +function. The factory function is registered with angular via {@link angular.service}. + +
    +// register a factory for a uniqueId service.
    +angular.service('uniqueId', function(){
    +  // calling the factory function creates the instance function
    +  var id = 0;
    +  return function(){
    +   // calling the counter instance function will return and increment the count
    +   return ++id;
    +  }
    +});
    +
    + +At run-time we can access the `uniqueId` service by looking it up with the service locator like +this: + +
    +// create new root scope which has the injector function `$service()`
    +var scope = angular.scope();
    +
    +// use the `$service` function to look up the service instance function
    +var idGenerator = scope.$service('uniqueId');
    +expect(idGenerator()).toBe(1);
    +
    +// subsequent lookups using the same root scope return the service instance
    +var idGenerator2 = scope.$service('uniqueId');
    +expect(idGenerator).toBe(idGenerator2);
    +
    +// since it is same instance calling idGenerator2 returns 2;
    +expect(idGenerator2()).toBe(2);
    +
    + +The {@link angular.service service} registry seems like a lot of work, so what are the benefits? To +answer this question, it’s important to realize that in large scale applications there are a lot of +services which are often dependent on each other, as in this example: + +
    +angular.service('gadgetFactory', function(uniqueId){
    +  return function(){
    +    return {gadgetId: uniqueId()};
    +  };
    +}, {$inject: ['uniqueId']});
    +
    + +Specifically, notice that the `gadgetFactory` takes `uniqueId` service in its arguments. It also +declares this dependency with the `$inject` property. There are several benefits to this approach: + +* There is no need for a `main` method for an application responsible for instantiating and wiring +these services. The order of service instantiation and wiring can be inferred by examining the +`$inject` annotations. +* It is easy to replace any one service with a different implementation without having to track down +all of the dependencies. This is useful in: + * Tests: when mocks of services are needed (for example using mock {@link angular.service.$xhr}.) + * Customization: when the service bundled with angular does not do exactly what the application +requires. + +More importantly, as we'll soon learn, controllers and other components of angular applications can +also declare their dependencies on services and these will be provided without explicitly looking +them up, but let's not get ahead of ourselves. + +Lastly, it is important to realize that all angular services are singletons – application singletons +to be more precise. This means that there is only one instance of a given service per injector. And +since angular is lethally allergic to the global state, it's absolutely possible to create multiple +injectors each with its own instance of a given service (but that is not typically needed, except in +tests where this property is crucially important). + + +## Service Locator and Scope + +The {@link 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 in your application. +The injector is responsible for caching the instances of services, but this cache is bound to the +scope. This means that different root scopes will 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 get this isolation by having each test create its own separate root scope. + +
    +// crate a root scope
    +var rootScope = angular.scope();
    +// accesss the service locator
    +var myService = rootScope.$service('myService');
    +
    + + + +# Dependency Injection in Controllers + +So far we have been talking about injector as a service locator. This is because we have been +explicitly calling the `$service` method to gain access to the service. Service locator is not +dependency injection since the caller is still responsible for retrieving the dependencies. *True +dependency injection is like Chuck Norris. Chuck does not ask for dependencies; he declares them.* + +The most common place to use dependency injection in angular applications is in +{@link angular.ng:controller controllers}. Here’s a simple example: + +
    +function MyController($route){
    +  // configure the route service
    +  $route.when(...);
    +}
    +MyController.$inject = ['$route'];
    +
    + +In this example, the `MyController` constructor function takes one argument, the +{@link 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, such as: + +
    +
    +
    + 
    + 
    +  ...
    + 
    +
    +
    + +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. + +
    +MyController.$inject = ['$route'];
    +
    + +The information in `$inject` is then used by the {@link angular.injector injector} to call the +function with the correct arguments. + + + +# Using Dependency Injection pragmatically + +At times you’ll need to use dependency injection pragmatically, usually when instantiating +controllers manually or writing unit tests. This section explains how to go about it. + +## Retrieving Services + +The simplest form of dependency injection is manual retrieval of scopes, known as service locator. +We say manual because we are asking the injector for an instance of the service (rather then having +the injector provide them to the function). This should be rare since most of the time the dependent +services should be injected into the controller using the `$inject` property array. + +
    +// create a root scope. The root scope will automatically have
    +// `$service` method defined which is configured with all services.
    +// Each instance of root scope will have separate instances of services.
    +var rootScope = angular.scope();
    +
    +// ask for a service explicitly
    +var $window = rootScope.$service('$window');
    +
    + + +## Creating Controllers using Dependency Injection + +In a typical angular application the dependency injection is most commonly used when creating +controllers. +
    +// declare our own service by registering a factory function.
    +angular.service('counter', function(){
    +  var count = 0;
    +  return function(){ return count++; };
    +});
    +
    +// example of a controller which depends on '$window' and 'counter' service
    +// notice that there is an extra unbound parameter 'name' which will not
    +// be injected and must be supplied by the caller.
    +function MyController($window, counter, name) {
    +}
    +
    +// we must declare the dependencies explicitly and in the same order as in
    +// the constructor function. This information is used by the dependency
    +// injection to supply the arguments.
    +// Notice the lack of 'name' argument which makes it an unbound argument.
    +MyController.$inject = ['$window', 'counter'];
    +
    +
    +// Create a root scope which creates the the injector
    +var rootScope = angular.scope();
    +
    +// use the '$new()' method instead of standard 'new' keyword operator to
    +// create an instance of MyController and have the dependency injection
    +// supply the arguments to the controller. The dependency injection only
    +// supplies the bound arguments in `$inject` all addition arguments are
    +// curried from the '$new', in our case 'Alexandria' is the argument which
    +// will be curried to the 'name' argument, while '$window' and 'counter'
    +// are supplied by the dependency injection.
    +var myController = rootScope.$new(MyController, 'Alexandria');
    +// NOTE: the returning controller will be a child scope of parent scope,
    +// in this case the root scope.
    +
    + + +## Calling functions and Curring of arguments + +NOTE: this section is quite lame. The concept it is trying to describe is more closely related to +scope#new than scope#$service. We need a better example to discuss here. Ideally a parent controller +creating a child controller imperatively via $new where the child controller's constructor function +declares a portion of its dependencies via $inject property, but another portion is supplied by the +caller of $new (e.g. parentCtrl.$new(ChildCtrl, configParam1, configParam2); + +Finally, you may need to call functions but have the `$inject` properties of the function be +supplied by the injector. + +
    +// create a root scope with the `$service` injector.
    +var rootScope = angular.scope();
    +
    +// given a function such as
    +function greet ($window, name) {
    +  $window.alert(this.salutation + ' ' + name);
    +}
    +greet.$inject = ['$window'];
    +
    +// you can call function 'greet' such that the injector supplies the
    +// '$window' and the caller supplies the function 'this' and the 'name'
    +// argument.
    +var fnThis = {salutation: 'Hello'}
    +rootScope.$service(greet, fnThis, 'world');
    +
    + + + +# Inferring `$inject` + +**EXPERIMENTAL: 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: + +
    +function myFn(a,b){}
    +expect(myFn.toString()).toEqual('function myFn(a,b){}');
    +
    + +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: + +
    +// 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'];
    +
    + +If angular does not find an `$inject` annotation on the function, then it calls the `.toString()` +and tries to infer what should be injected using the following rules: + +* any argument starting with `$` is angular service and will be added to `$inject` property array. +* any argument ending with `_` will be added to the `$inject` property array but we strip 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 curring}) + +**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. diff --git a/docs/content/guide/index.ngdoc b/docs/content/guide/index.ngdoc new file mode 100644 index 00000000..2798210d --- /dev/null +++ b/docs/content/guide/index.ngdoc @@ -0,0 +1,37 @@ +@workInProgress +@ngdoc overview +@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) +* {@link 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. diff --git a/docs/content/guide/overview.ngdoc b/docs/content/guide/overview.ngdoc new file mode 100644 index 00000000..61c58435 --- /dev/null +++ b/docs/content/guide/overview.ngdoc @@ -0,0 +1,337 @@ +@ngdoc overview +@name Developer Guide: Overview +@description + + +* What Is Angular? +* The Angular Philosophy +* Anatomy Of An Angular App +* Why You Want Angular +* Angular's Ancestors +* Watch a Presentation About Angular + + + +# 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 kind of depends on where you're coming from... + +* If you're a web designer, you might perceive angular to be a sweet {@link guide.template +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 +guide.compiler compiler} that runs in your browser. This compiler teaches your browser new tricks. + +So then, angular's not just a templating system, but you can create fantastic templates with it; +angular's not just a web framework, but it has a very nice one; and angular's not just an +extensible HTML compiler, but it has one of those too. Let's put it this way: angular includes +these parts along with some others; it evolved naturally from earlier occurrences of these forms; +and thus angular is something far greater than the sum of its parts. It sounds like... it's alive! + +## An Intro By Way of 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 doing DOM updates, writing listeners, and +writing input validators, all to do something as simple as implementing a form!? You either don't +want to go there in the first place or you've been there and the thrill is gone. + +You could even be muttering to yourself as you hack another callback, "This is like building my own +bike from scratch every time I want to ride to the store." But let's say a clever friend, who keeps +tabs on these sorts of things, told you to check out angular. + +So now here you are checking out angular, and here is a simple example. Note that it features only +the templating aspect of angular, but this should suffice for now to quickly demonstrates how much +easier life can be with angular: + + + +

    Bigg Bike Shop

    +
    + Invoice: +
    +
    + + + + + + + +
    QuantityCost
    +
    + Total: {{qty * cost | currency}} +
    +
    + +
    + +Go ahead, try out the Live Preview above. "Well I _declare_! It's a fully functioning form, with +an instantly updating display, and input validation." Speaking of being declarative, let's walk +through the example and look at the angular-related lines to see what's going on around here. + +In line __2__ of the example, we let the browser know about the angular namespace: + + 2 + +This ensures angular runs nicely in all major browsers. + +In line __3__ we do two angular setup tasks inside a ` + +Lines __14__ and __15__ set up one side of angular's very cool two-way data binding, as well as +demonstrate some easy input validation: + + 14 Quantity: + 15 Cost: + +These input widgets look normal enough, but consider these points: + +* Remember the `ng:autobind` directive from line 3? 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" part of the Model-View-Controller design pattern. +* Note the angular directives, {@link angular.widget.@ng:validate ng:validate} and {@link +ngular.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 a plainly irritated 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 line #__19__: + + 19 Total: {{qty * cost | currency}} + +What's with the curly braces? Those curly braces are your friend. This notation, `{{ _expression_ +}}`, is a bit of built-in angular {@link angular.markup markup}, a shortcut that you use to display +data. The expression within curly braces gets transformed by the angular compiler into an angular +directive ({@link angular.directive.ng:bind ng:bind}). The expression itself can be a combination +of both an expression and a {@link angular.filter filter}: `{{ expression | filter }}`. + +In our example above, we're saying, "Bind the data we got from the input widgets to the display, +multiply them together, and format the resulting number into something 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 clearly the way to +go 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 it by simply adding text to the HTML template, saving the code, and refreshing your +browser (this here is declarative): + +
    +Hello
    +
    + +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: + +
    +var label = new Label();
    +label.setText('Hello');
    +label.setClass('label');
    +parent.addChild(label);
    +
    + +That looks like, let's see, do some math, factor out the `
    `s, carry the one, ummm...  a little
    +bit 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
    +what angular isn't:
    +
    +* 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 with 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.
    +
    +
    +
    +# Anatomy Of An Angular App
    +
    +This section describes the parts of an angular app in more detail.
    +
    +## Templates
    +
    +{@link guide.template Templates} are the part of angular that makes it easy and fun to create the
    +UI for your web apps.  With angular's templates you can create a dynamic UI using only HTML and
    +CSS, but now you can add your own elements, attributes, and markup.  The angular compiler reads the
    +"angularized" HTML when your page loads, and follows the instructions in there to generate a
    +dynamic page for you.  This is the View part of MVC. "But wait there's more": since the compiler is
    +extensible, you can build your own declarative language on top of HTML!
    +
    +## Application Logic and Behavior
    +
    +Application Logic and Behavior, which you define in JavaScript, is the C in MVC. With angular you
    +write the logic (the controllers) for your app, but because angular takes care of reflecting the
    +state of the model in the view, you don't have to write listeners or DOM manipulators. This feature
    +makes your application logic very easy to write, test, maintain, and understand.
    +
    +## Data
    +
    +In an angular app, all of your data is referenced from inside of a {@link angular.scope scope}.
    +The scope is the data Model, the M in the MVC pattern. A scope is a JavaScript object that has
    +watcher functions that keep tabs on the data that is referenced from that scope. The data could be
    +one or more Javascript objects, arrays, or primitives, it doesn't matter.  What matters is that
    +these are all referenced by the scope.
    +
    +This "scope thing" is how angular takes care of keeping your data model and your UI in sync.
    +Whenever something occurs to change the state of the scope, angular immediately reflects that
    +change in the UI, and vice versa.
    +
    +In addition to the three components described above (the MVC bits), angular comes with a set of
    +{@link angular.service Services} that are very helpful for building web apps. The services include
    +the following features:
    +
    +* You can extend and add application-specific behavior to services.
    +* Services include Dependency-Injection, XHR, caching, URL routing, and browser abstraction.
    +
    +The following illustration shows the parts of an angular application and how they work together:
    +
    +
    +
    +
    +
    +# 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, knock yourself out.
    +* **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 (gah!) 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.
    +
    +
    +
    +# Angular's Ancestors
    +
    +Where does angular come from? What events led to the inevitability of the appearance of something
    +like angular?
    +
    +## First There Was HTML
    +
    +HTML was initially designed long, long ago, in the great year of 1989, with the intention to create
    +a markup language for sharing scientific documents over the network. Yes, yes, certainly there was
    +SGML even before that, but it was so difficult that even esteemed scientists balked at using it.
    +Thankfully, Tim Berners-Lee saved all of us from that pain with his much friendlier HTML.
    +`Thank You, TB-L!`.
    +
    +## Then There Was JavaScript
    +
    +Fast forward to 1995: JavaScript was invented. This was done with the best of intentions!  But in
    +practice it initially served mainly to annoy Internet users with cheap effects that "enhanced"
    +static HTML documents.
    +
    +Fast forward to the mid 2000s, when a new breed of back-then-considered-rich web applications
    +started to appear on the web. These were built with HTML, JavaScript, and CSS, and featured less
    +annoying and more impressive effects. Can you recall the first time you saw apps like Gmail, or
    +Google Maps, and you couldn't believe everything that was going on in the browser?
    +
    +## And JavaScript Prevailed
    +
    +As of this writing, in 2011, people are building still richer and more interactive web applications
    +that often rival their desktop counterparts. And yet they are essentially still working with
    +technology and programming primitives that were used decades ago for the creation of static
    +documents with cheap graphic effects. At the same time, the web is HUGE now, and we
    +can't just abandon the technologies it was built with. Applets, Flash and Silverlight tried it, and
    +in some ways succeeded. Yet many would argue that in reality they failed, because they tried to
    +work _around_ the web instead of working _with_ it.
    +
    +## And Then There Was Angular
    +
    +Angular recognizes the strengths of the existing "static" web technologies, as well as their
    +deficiencies.  At the same time, angular is learning from the failures of other technologies that
    +tried, or are trying, to work around the web.
    +
    +For these reasons angular plays to the strengths of established web technologies, instead of
    +bypassing them. Angular sets out the goal of increasing the abstraction and programming primitives
    +that developers use to build web applications, so as to better reflect the needs of modern web
    +applications and their developers.
    +
    +
    +
    +# 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.
    +
    +
    + 
    + 
    + 
    + 
    +
    +
    +{@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}
    diff --git a/docs/content/guide/template.ngdoc b/docs/content/guide/template.ngdoc
    new file mode 100644
    index 00000000..ae9bba92
    --- /dev/null
    +++ b/docs/content/guide/template.ngdoc
    @@ -0,0 +1,22 @@
    +@workInProgress
    +@ngdoc overview
    +@name Developer Guide: Template
    +@description
    +#Template
    +
    +You can think of a template in angular as a domain-specific language that you can use to easily
    +build the view of your web application. You create a template by writing HTML and CSS, and you can
    +add any constructs that you want to the HTML. This means that you can attach rendering and behavior
    +to any HTML element, attribute or markup text.
    +
    +In addition to writing HTML and CSS, you can also use the following angular constructs to create
    +your template:
    +
    + * **Directive** - XML attributes that augment an existing DOM element.
    + * **Markup** - Lets you 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.
    + * **Filter** - Lets you format your data for display to the user.
    + * **Widget** - Lets you create new DOM elements that the browser doesn't already understand.
    + * **Validator** - Lets you validate user input.
    + * **Formatter** - Lets you format the input object into a user readable view.
    diff --git a/docs/content/guide/testing.ngdoc b/docs/content/guide/testing.ngdoc
    new file mode 100644
    index 00000000..bb3a1441
    --- /dev/null
    +++ b/docs/content/guide/testing.ngdoc
    @@ -0,0 +1,8 @@
    +@workInProgress
    +@ngdoc overview
    +@name Developer Guide: Testing
    +@description
    +
    +# Testing Angular Applications
    +
    +to be written...
    diff --git a/docs/content/intro/contribute.ngdoc b/docs/content/intro/contribute.ngdoc
    new file mode 100644
    index 00000000..43d17283
    --- /dev/null
    +++ b/docs/content/intro/contribute.ngdoc
    @@ -0,0 +1,233 @@
    +@ngdoc overview
    +@name 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}. Your contributions are
    +always welcome. When working with `angular` source base, please follow the guidelines provided on
    +this page.
    +
    +* Contributing to Source Code
    +* Applying Code Standards
    +* Checking Out and Building `Angular`
    +* Submitting Your Changes
    +
    +
    +
    +
    +# 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 pull
    +request.
    +
    +
    +
    +
    +# 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 specs.
    +* 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.
    +
    +
    +
    +# 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:/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-.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.
    +
    +
    +
    +## Running the Unit Test Suite
    +
    +Our unit and integration tests are written with Jasmine and executed with JsTestDriver.  To run the
    +tests:
    +
    +1. To start the JSTD server, run:
    +
    +        ./server.sh
    +
    +2. To capture one or more browsers, go to this website:
    +
    +        http://localhost:9876/
    +
    +3. To trigger a test execution, run:
    +
    +         ./test.sh
    +
    +4. To automatically run the test suite each time one or more of the files in the project directory
    +is changed, you can install `watchr` and then run:
    +
    +        watchr watchr.rb
    +
    +5. To view the output of each test run, you can tail this log file:
    +
    +        ./logs/jstd.log
    +
    +
    +## Running the End2End Test Suite
    +
    +To run the End2End test suite:
    +
    +1. Start the local web server.
    +2. In a browser, go to:
    +
    +        http://localhost:8000/build/docs/docs-scenario.html
    +
    +   The tests are executed automatically.
    +
    +
    +
    +
    +# 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:
    +
    +            git push origin :my-fix-branch
    +
    +  * To check out the master branch, run:
    +
    +            git checkout master
    +
    +  * To delete a local branch, run:
    +
    +            git branch -D my-fix-branch
    +
    +  * To update your master with the latest upstream version, run:
    +
    +            git pull --ff upstream master
    +
    +That's it! Thank you for your contribution!
    diff --git a/docs/content/intro/downloading.ngdoc b/docs/content/intro/downloading.ngdoc
    new file mode 100644
    index 00000000..a02b3367
    --- /dev/null
    +++ b/docs/content/intro/downloading.ngdoc
    @@ -0,0 +1,70 @@
    +@workInProgress
    +@ngdoc overview
    +@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  urls.
    +
    +There are two kinds of urls you care about:
    +
    +* http://code.angularjs.org/angular-.js
    +* http://code.angularjs.org/angular-.min.js
    +
    +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:
    +
    +
    +  
    +  
    +    
    +      My Angular App
    +      
    +    
    +    
    +    
    +  
    +
    + + +# 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 , you'll see a directory listing with all 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. Once you navigate +to one of these directories you'll see the following list of files: + +* `angular-.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-.min.js` - This is a minified and obfuscated version of +`angular-.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-.tgz` - This is a tarball archive which contains all the other files released +for this angular version. Use this file to get everything in a single download. + +* `angular-ie-compat-.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-.js` or `angular-.min.js`. + +* `angular-mocks-.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-.js` is loaded. + +* `angular-scenario-.js` - This file is a very nifty JavaScript file, which allows you to +write and execute end to end tests for angular applications. + +* `docs-` - this directory contains all the files that compose the + documentation app. These files are handy to see the older version of +our docs, or even more importantly, view the docs offline! diff --git a/docs/content/intro/faq.ngdoc b/docs/content/intro/faq.ngdoc new file mode 100644 index 00000000..dd06d961 --- /dev/null +++ b/docs/content/intro/faq.ngdoc @@ -0,0 +1,81 @@ +@workInProgress +@ngdoc overview +@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 an HTML5 tag? + +No, is not an HTML5 tag. angular is an orthogonal project to HTML5; you can use the two +together. + +### Is angular a {library, framework, DOM manipulation library, widget library, native plugin}? + +No, angular is none of these. You don't call its functions, it does not call your functions, +it does not provide a way to manipulate DOM, but does provide primitives to create UI projections +of your data. There are lots of existing widget libraries which you can integrate with angular. +It is 100% JavaScript, 100% client side and compatible with both desktop and mobile browsers. + +### Do I need to worry about security holes in angular? + +Like with any technology, angular is not impervious to attack. angular does, however, provide +built-in protection from basic security holes including cross-site scripting and HTML injection +attacks. angular does round-trip escaping on all strings for you. + +### Can I download the source, build, and host the angular environment locally? + +Yes. See instructions in {@link downloading 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 +the compilation step produces a live view. This means you, the developer, don't need to write +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 +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. + +### How can I learn more about angular? + +Watch the July 28, 2010 talk +"{@link http://www.youtube.com/watch?v=elvcgVSynRg| Angular: A Radically Different Way of Building AJAX Apps}". + +### How is angular licensed? + +The MIT License. diff --git a/docs/content/intro/started.ngdoc b/docs/content/intro/started.ngdoc new file mode 100644 index 00000000..a505b471 --- /dev/null +++ b/docs/content/intro/started.ngdoc @@ -0,0 +1,146 @@ +@workInProgress +@ngdoc overview +@name Getting Started +@description + +# Hello World! + +A great way for you to get started with `angular` is to create the tradtional +"Hello World!" app: + +1. In your favorite text editor, create an HTML file + (for example, `helloworld.html`). +2. From the __Source__ box below, copy and paste the code into your HTML file. + (Double-click on the source to easily select all.) +3. Open the file in your web browser. + + + + Hello {{'World'}}! + + + +The resulting web page should look something like the following: + + + +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): + +
    +    
    +
    + +The next line downloads the `angular` script, and instructs `angular` to process +the entire HTML page when it is loaded: + +
    +    
    +
    + +(For details on what happens when `angular` processes an HTML page, +see {@link guide.bootstrap Bootstrap}.) + +Finally, this line in the `` of the page is the template that describes +how to display our greeting in the UI: + +
    +    Hello {{'World'}}!
    +
    + +Note the use of the double curly brace markup (`{{ }}`) to bind the expression to +the greeting text. Here the expression is the string literal 'World'. + +Next let's look at a more interesting example, that uses `angular` to +bind a dynamic expression to our greeting text. + +# Hello World! + +This example demonstrates `angular`'s two-way data binding: + +1. Edit the HTML file you created in the "Hello World!" example above. +2. Replace the contents of `` with the code from the __Source__ box below. +3. Refresh your browswer window. + + + + Your name: +
    + Hello {{yourname}}! +
    +
    + +After the refresh, the page should look something like this: + + + +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 + `yourname`. +* The double curly braces notation binds the variable `yourname` to the + greeting text. + +* You did not need to explicitly register an event listener or define an event + handler for events! + +Now try typing your name into the input box, and notice the immediate change to +the displayed greeting. This demonstrates the concept of `angular`'s +{@link guide.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 +reflected in the greeting text (the other direction). + + +# Anatomy of an `angular` App + +This section describes the 3 parts of an `angular` app, and explains how they +map to the Model-View-Controller design pattern: + +## Templates + +Templates, which you write in HTML and CSS, serve as the View. You add elements, +attributes, and markup to HTML, which serve as instructions to the `angular` +compiler. The `angular` compiler is fully extensible, meaning that with angular +you can build your own declarative language on top of HTML! + +## Application Logic and Behavior + +Application Logic and Behavior, which you define in JavaScript, serve as the +Controller. With `angular` (unlike with standard AJAX applications) you don't +need to write additional listeners or DOM manipulators, because they are built-in. +This feature makes your application logic very easy to write, test, maintain, and +understand. + +## Scope + +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: + + + +In addition, `angular` comes with a set of Services, which have the following +properties: + +* The services provided are very useful for building web applications. +* You can extend and add application-specific behavior to services. +* Services include Dependency-Injection, XHR, caching, URL routing, + and browser abstraction. + +# Where To Go Next + +* For 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}. diff --git a/docs/content/intro/testimonials.ngdoc b/docs/content/intro/testimonials.ngdoc new file mode 100644 index 00000000..2e333f4b --- /dev/null +++ b/docs/content/intro/testimonials.ngdoc @@ -0,0 +1,33 @@ +@ngdoc overview +@name Testimonials +@description + + +## John Hardy +> Also I want to pass on my compliments to Misko and Igor for this fantastic project. I'm currently +> rewriting a server-side web application to use this system. I am constantly astounded at how much +> simpler it is to do it this way and I still consider myself a learner. + +> This is without question the most productive approach to building webapps that I have seen. + +> The last time I had a coding epiphany was discovering the power and simplicity of JQuery. This is +> way better than that. + +> I'm interested in promoting this library as widely as possible. I understand that you are still +> developing it and I still have a long way to go before I really understand everything but I think +> you really have something here. + + +## Jerry Jeremiah +> angular is the best thing I have used in a long time. I am having so much fun, even thought it is +> probably obvious that dynamic web sites are new to me (my experience is more in the back end +> embedded world...) + + +## Dobrica Pavlinusic +> Thanks to great help I received at this list, I was basically able to accomplish my goal to write +> simple conference submission application within a week of first git clone of angular source from +> github. + +> I think it might be useful to summarize my experience here, especially for people who are still +> wondering if angular is worth a try. Executive summary is: **yes it is!** \ No newline at end of file diff --git a/docs/content/tutorial/index.ngdoc b/docs/content/tutorial/index.ngdoc new file mode 100644 index 00000000..b430b248 --- /dev/null +++ b/docs/content/tutorial/index.ngdoc @@ -0,0 +1,172 @@ +@workInProgress +@ngdoc overview +@name Tutorial +@description + +A great way to get introduced to angular is to work through the {@link tutorial.step_00 angular +tutorial}, which walks you through the construction of an angular web app. The app you will build +in the tutorial is loosely based on the {@link http://www.google.com/phone/ Google phone gallery +app}. The {@link http://angular.github.com/angular-phonecat/step-11/app/ end result of our effort} +is visually simpler, but demonstrates many of the angular features without distractions in the +form of CSS code. + +This tutorial app ends up like a Google phone gallery app, but is originally based on the {@link +https://github.com/angular/angular-seed angular-seed project}. The angular seed app isn't +necessary for building angular apps, but it helps you get started quickly and makes the +development and testing process much easier. Angular-seed includes a simple example, the latest +angular libraries, test libraries, and scripts. It provides all of these in an environment that +is pre-configured for developing a typical web app. + +Once you set up your tutorial environment, you should be able to get through the material in less +than a day and you'll have fun doing it. More experienced coders may be able to zip through the +exercises in an afternoon. In any case, we promise that your time will be well spent! + +When you finish the tutorial you will be able to: + +* Create a simple dynamic application that works in any browser +* Define the differences between angular and common JavaScript frameworks +* Understand angular expressions +* Understand how data binding works in angular +* Use the angular-seed project to quickly boot-strap your own projects +* Create and run tests +* Identify resources for learning more about angular + +You can work through the tutorial in any of the following ways: + +* Using Git. Use the Git versioning system to get the files for each step. +* Using Snapshots. Download snapshots (files for each step of the +tutorial) and tinker with them. +* Reading the Examples. Read through the examples, and inspect +results and code on our server. + +The first two ways (Git and snapshots) give you a fuller experience, in that you can run the unit +and end-to-end tests in addition to the tutorial app. They also give you the ability to play +around with the code and get instant feedback in your browser. The last way (reading through the +tutorial online) requires no setup on your machine, but you can't run the tests, and it won't be +as easy to play around with the code. + + +# Prerequisites for Git and Snapshots + +To run the tutorial app and tests on your machine (using Git or the snapshots) you will need the +following: + +* You need to be running on a Mac or Linux machine. +* An http server running on your system. If you don't already have one installed, you can install +`node.js` ({@link https://github.com/joyent/node/wiki/Installation node.js install}) or another +http sever (such as Apache, etc.). +* Java. This is required for running tests. Angular itself doesn't require Java. +* A modern browser (including IE8+). Needed for viewing and debugging code. +* A text editor of your choice. + + +# Using Git + +The following instructions are for developers who are comfortable with Git's versioning system: + +1. Check to be sure you have all of the prerequisites on your system. + +2. Clone the angular-phonecat repository located at {@link +https://github.com/angular/angular-phonecat angular-phonecat} by running the following command in +a terminal: + + git clone git://github.com/angular/angular-phonecat.git + + This will create a directory called `angular-phonecat`. + +3. In terminal, navigate to the `angular-phonecat` directory and run: + + git checkout step-0 + + (You can run `git checkout step-[0-11]` to go to any of the steps in the tutorial). + +4. To see the app running in a browser, do the following: + * __For node.js users:__ + 1. 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. Run `./scripts/web-server.js` to start the app server. + 3. Navigate in your browser to + http://localhost:[*port-number*]/[*context-path*]/app/index.html. + +5. To see tests running in a browser, do the following: + * __For node.js users:__ + 1. Run `./scripts/test-server.sh` to start the test web server. + 2. Open a browser window for the tests, navigate to http://localhost:9876, and choose + "strict mode". + * __For other http servers:__ + 1. Configure the server to serve the files in the `angular-phonecat` directory. + 1. Run `./scripts/test-server.sh` to start the test web server. + 3. Navigate in your browser to http://localhost:[*port-number*]/, and choose "strict mode". + + + + +# Using Snapshots + +Snapshots are the sets of files that reflect the state of the tutorial app at each step. These +files include the HTML, CSS, and JavaScript for the app, plus Jasmine JavaScript files and Java +libraries for the test stack. These will let you run the tutorial app and tests, without requiring +knowledge of Git. You can download and install the snapshot files as follows: + +1. Check to be sure you have all of the prerequisites on your system. + +2. Navigate to [*the angular server*], and download and unzip [*the snapshot file*] to an +[*install-dir*] of your choosing. + +3. Change directories to [*install-dir*]/sandbox. + +4. Run the following command: + * `./goto_step.sh 0` + + You have to start out at the beginning, which is Step 0. After you set up Step 0, you can skip + around between any steps. + +1. To see the app running in your browser, do the following: + * __For node.js users:__ + 1. Run `./scripts/web-server.js` to run the web server. + 2. Open a browser window for the app and navigate to http://localhost:8000/app/index.html. + 3. Open a browser window for the tests, navigate to http://localhost:9876, and choose + "strict mode". + + * __For other http servers:__ + 1. Configure servers to serve the app and test files in the [*install-dir*]/sandbox. + 2. Start the server. + 3. Navigate in your app browser to + http://localhost:[*port-number*]/[*context-path*]/app/index.html. + 4. Navigate in your test browser to http://localhost:[*port-number*] and choose "strict + mode". + +1. To view the tutorial app at different steps, run `./goto_step.sh [0-11]` and then refresh your +browser. For example, say you're on Step 5 of the tutorial, and you want to see the app in action: + + 1. Run `goto_step.sh 5` from the command line in the `sandbox` directory. + 1. Refresh your app browser. + + +# Reading the Examples + +If you don't want to set up anything on your local machine, you can read through the tutorial and +inspect the tutorial files on our servers; doing this will give you a good idea of what angular +does, but you won't be able to make any code changes and experiment on your own. + +To see the running app at each tutorial step, click the "Example" link at the top or bottom of +each tutorial page. + +To view the code differences between tutorial steps, click the Code Diff link at top or bottom of +each tutorial page. Additions are highlighted in green; deletions are highlighted in red. + + +# Relative URLs +Throughout the tutorial, we use relative URLs to refer to files hosted on our local http server. +The absolute URL depends on your configuration. For example, if you are using the node.js server, +`app/index.html` translates to: + + http://localhost:8000/app/index.html + +If you are using your own http server running on port 8080 and the tutorial files are hosted at +`/angular_tutorial`, `app/index.html` translates to: + + http://localhost:8080/angular_tutorial/app/index.html diff --git a/docs/content/tutorial/step_00.ngdoc b/docs/content/tutorial/step_00.ngdoc new file mode 100755 index 00000000..e506fcaf --- /dev/null +++ b/docs/content/tutorial/step_00.ngdoc @@ -0,0 +1,77 @@ +@workInProgress +@ngdoc overview +@name Tutorial: Step 0 +@description + + + + + + + + + +
    {@link tutorial Previous}{@link http://angular.github.com/angular-phonecat/step-0/app Example}{@link tutorial Tutorial Home}Code Diff{@link tutorial.step_01 Next}
    + +The following sample code is our starting point. It is a static HTML page that displays next to +nothing, but it has everything we need to proceed. You can think of this bit of code as our +prototype template, consisting of basic HTML tags with a pair of angular specific attributes. + +__`app/index.html`:__ +
    +
    +
    +
    +  
    +  my angular app
    +  
    +
    +
    +
    +  Nothing here yet!
    +
    +  
    +
    +
    +
    + +## Discussion: + +Although our app doesn't appear to do anything dynamic, note the following: + +* __... `xmlns:ng="http://angularjs.org"` ...__ This `xmlns` declaration for the `ng` namespace +must be specified if you use XHTML, or if you are targeting IE older than 9 (regardless of whether +you are using XHTML or HTML). + +* __` + + This will download the angular script from the angular server instead of from a local file. + +* To try this code out in your browser, you need to navigate to the step-0 page (you are currently +on Step 0 of the tutorial). If your http server is running, navigate to `app/index.html`. +Remember, this is a relative URL (see the Relative URL section in {@link tutorial Tutorial}). The +browser will display the same thing as you would see if you go to +http://angular.github.com/angular-phonecat/step-0/app (accessible from Example link at the bottom +of the page). + +Now we can move on and add some content to our developing web app. + + + + + + + + + +
    {@link tutorial Previous}{@link http://angular.github.com/angular-phonecat/step-0/app Example}{@link tutorial Tutorial Home}Code Diff{@link tutorial.step_01 Next}
    diff --git a/docs/content/tutorial/step_01.ngdoc b/docs/content/tutorial/step_01.ngdoc new file mode 100755 index 00000000..e22adc20 --- /dev/null +++ b/docs/content/tutorial/step_01.ngdoc @@ -0,0 +1,88 @@ +@workInProgress +@ngdoc overview +@name Tutorial: Step 1 +@description + + + + + + + + +
    {@link tutorial.step_00 Previous}{@link http://angular.github.com/angular-phonecat/step-1/app Example}{@link tutorial Tutorial Home} +{@link https://github.com/angular/angular-phonecat/compare/step-0...step-1 Code Diff}{@link tutorial.step_02 Next}
    + +Now that we have the basic ingredients in place, let's add some basic information about two cell +phones to our app. + +Note: We will usually include only the new code that we added for each step. In this and +subsequent examples, we will leave out code from the previous step that hasn't changed, for +example: + + ... + + ... + +Let's add the following code to `index.html`: + +__`app/index.html`:__ +
    +
    +...
    +  Google Phone Gallery
    +...
    +
    +...
    +  
      +
    • + Nexus S +

      + Fast just got faster with Nexus S. +

      +
    • +
    • + Motorola XOOM™ with Wi-Fi +

      + The Next, Next Generation tablet. +

      +
    • +
    +... +
    + +## Discussion: + +* It's a static web page! We displayed info about two phones! Yay. + +* For those of you playing along at home on your own web servers, did you switch to Step 1 and +refresh your browsers? + + * __{@link tutorial Using Git:}__ + + From your `angular-phonecat` directory, run this command: + + git checkout step-1 + + * __{@link tutorial Using Snapshots:}__ + + From `[install directory]/sandbox`, run this command: + + ./goto_step.sh 1 + +* Now would be a good time to open up `app/index.html` in your browser and see the current state +of our "application". It's not very exciting, but that's ok. + +When you're ready, let's move on and start using some angular features to turn this static page +into a dynamic web app. + + + + + + + + + +
    {@link tutorial.step_00 Previous}{@link http://angular.github.com/angular-phonecat/step-1/app Example}{@link tutorial Tutorial Home} +{@link https://github.com/angular/angular-phonecat/compare/step-0...step-1 Code Diff}{@link tutorial.step_02 Next}
    diff --git a/docs/content/tutorial/step_02.ngdoc b/docs/content/tutorial/step_02.ngdoc new file mode 100755 index 00000000..50fbd240 --- /dev/null +++ b/docs/content/tutorial/step_02.ngdoc @@ -0,0 +1,137 @@ +@workInProgress +@ngdoc overview +@name Tutorial: Step 2 +@description + + + + + + + + +
    {@link tutorial.step_01 Previous}{@link http://angular.github.com/angular-phonecat/step-2/app Example}{@link tutorial Tutorial Home}{@link https://github.com/angular/angular-phonecat/compare/step-1...step-2 Code +Diff}{@link tutorial.step_03 Next}
    + +In the last step, we remembered what a basic, static web page looks like, and now we want to get +dynamic. There are many ways to do this, but an important feature of angular is the incorporation +of the principles behind {@link http://en.wikipedia.org/wiki/Model–View–Controller the MVC design +pattern} into client-side web apps. With that in mind, let's use a little angular and a little +JavaScript to add Model, View, and Controller components to our app, and change the static page +into one that is dynamically generated. + +Our __View__ component is constructed by angular from this template: + +__`app/index.html`:__ +
    +...
    +
    +
    +  
      +
    • + {{phone.name}} +

      {{phone.snippet}}

      +
    • +
    + + + + +... +
    + +Our data __Model__ (a short list of phones in object literal notation) is instantiated within our +__Controller__ function (`PhoneListCtrl`): + +__`app/js/controllers.js`:__ +
    +/* App Controllers */
    +
    +function PhoneListCtrl() {
    +  this.phones = [{"name": "Nexus S",
    +                  "snippet": "Fast just got faster with Nexus S."},
    +                 {"name": "Motorola XOOM™ with Wi-Fi",
    +                  "snippet": "The Next, Next Generation tablet."},
    +                 {"name": "MOTOROLA XOOM™",
    +                  "snippet": "The Next, Next Generation tablet."}];
    +}
    +
    + +The "Angular way" urges us to test as we develop: + +__`test/unit/controllersSpec.js`:__ +
    +/* jasmine specs for controllers go here */
    +describe('PhoneCat controllers', function() {
    +
    +  describe('PhoneListCtrl', function(){
    +
    +    it('should create "phones" model with 3 phones', function() {
    +      var ctrl = new PhoneListCtrl();
    +      expect(ctrl.phones.length).toBe(3);
    +    });
    +  });
    +});
    +
    + +## Discussion: + +So what were our changes from Step 1? + +* __View template:__ 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}}`: + + * The `ng:repeat="phone in phones"` statement in the `
  • ` tag is an angular repeater. It + tells angular to create a `
  • ` element for each phone in the phones list, using the first + `
  • ` tag as the template. + + * The curly braces around `phone.name` and `phone.snippet` are an example of {@link + angular.markup angular markup}. The curly braces are shorthand for the angular directive + {@link angular.directive.ng:bind ng:bind}. They 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. + +* __Controller:__ At this point, it doesn't appear as if our controller is doing very much +controlling, but it is playing a crucial role: providing context for our data model so we can +establish data-binding between the model and the view. Note in the following how we connected the +dots between our presentation, data, and logic components: + + * The name of our controller function (in the JavaScript file `controllers.js`) matches the + {@link angular.directive.ng:controller ng:controller} directive in the `` 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 ` + + {@link tutorial.step_01 Previous} + {@link http://angular.github.com/angular-phonecat/step-2/app Example} + {@link tutorial Tutorial Home} +{@link https://github.com/angular/angular-phonecat/compare/step-1...step-2 Code +Diff} + {@link tutorial.step_03 Next} + + diff --git a/docs/content/tutorial/step_03.ngdoc b/docs/content/tutorial/step_03.ngdoc new file mode 100755 index 00000000..4333636d --- /dev/null +++ b/docs/content/tutorial/step_03.ngdoc @@ -0,0 +1,108 @@ +@workInProgress +@ngdoc overview +@name Tutorial: Step 3 +@description + + + + + + + + +
    {@link tutorial.step_02 Previous}{@link http://angular.github.com/angular-phonecat/step-3/app Example}{@link tutorial Tutorial Home}{@link + https://github.com/angular/angular-phonecat/commit/a03815f8fb00217f5f9c1d3ef83282f79818e706 Code + Diff}{@link tutorial.step_04 Next}
    + +We did a lot of work in laying the foundation of our app in the last step, so now we'll do +something simple, and add full text search. We will also write an end-to-end test, because a good +end-to-end test is a good friend. It stays with your app, keeps an eye on it, and quickly detects +regressions. + +__`app/index.html`:__ +
    +...
    +   Fulltext Search: 
    +
    +  
      +
    • + {{phone.name}} +

      {{phone.snippet}}

      +
    • +
    +... +
    +__`test/e2e/scenarios.js`:__ +
    +/* jasmine-like end2end tests go here */
    +describe('PhoneCat App', function() {
    +
    +  describe('Phone list view', function() {
    +
    +    beforeEach(function() {
    +      browser().navigateTo('../../app/index.html');
    +    });
    +
    +
    +    it('should filter the phone list as user types into the search box', function() {
    +      expect(repeater('.phones li').count()).toBe(3);
    +
    +      input('query').enter('nexus');
    +      expect(repeater('.phones li').count()).toBe(1);
    +
    +      input('query').enter('motorola');
    +      expect(repeater('.phones li').count()).toBe(2);
    +    });
    +  });
    +});
    +
    + +## Discussion: + +We continued using the same controller that we set up in Step 2, but we added the following +features to our app: + +* __Search Box:__ A standard HTML `` tag combined with angular's {@link +angular.Array.filter $filter} utility (added to the repeater) lets a user type in search criteria +and immediately see the effects of their search on the phone list. This new code demonstrates the +following: + + * Two way Data-binding. This is one of the core features in angular. When the page loads, + angular binds the name of the input box to a variable of the same name in the data model and + keeps the two in sync. + +In this example, the data that you type into the input box (named __`query`__) is immediately +available as a filter input in the list repeater (`phone in phones.$filter(`__`query`__`)`). +Whenever the data model changes and this change causes the input to the repeater to change, the +repeater will efficiently update the DOM to reflect the current state of the model. + + * Use of `$filter` in a template. The `$filter` function is one of several built-in {@link + angular.Array angular functions} that augment JavaScript arrays during their evaluation as + angular expressions. In {@link guide.expression angular expressions}, these array utilities are + available as array methods. (They are prefixed with a $ to avoid naming collisions.) + + * `ng:repeat` automatically shrinks and grows the number of phones in the View, via DOM + manipulation that is completely transparent to the developer. If you've written any DOM + manipulation code, this should make you happy. + +* __CSS:__ We added in some minimal CSS to the file we set up in Step 0: `./css/app.css`. + +* __Testing:__ To run the end to end test, open http://localhost:8000/test/e2e/runner.html in +your browser. This end-to-end test shows the following: + + * Proof that the search box and the repeater are correctly wired together. + + * How easy it is to write end-to-end tests. This is just a simple test, but the point here is + to show how easy it is to set up a functional, readable, end-to-end test. + + + + + + + + + +
    {@link tutorial.step_02 Previous}{@link http://angular.github.com/angular-phonecat/step-3/app Example}{@link tutorial Tutorial Home}{@link + https://github.com/angular/angular-phonecat/commit/a03815f8fb00217f5f9c1d3ef83282f79818e706 Code + Diff}{@link tutorial.step_04 Next}
    diff --git a/docs/content/tutorial/step_04.ngdoc b/docs/content/tutorial/step_04.ngdoc new file mode 100755 index 00000000..0589ba75 --- /dev/null +++ b/docs/content/tutorial/step_04.ngdoc @@ -0,0 +1,161 @@ +@workInProgress +@ngdoc overview +@name Tutorial: Step 4 +@description + + + + + + + + +
    {@link tutorial.step_03 Previous}{@link http://angular.github.com/angular-phonecat/step-4/app Example}{@link tutorial Tutorial Home}{@link https://github.com/angular/angular-phonecat/compare/step-3...step-4 Code +Diff}{@link tutorial.step_05 Next}
    + +In this step, we add a feature that lets our users choose which way to order the phone list. + +__`app/index.html`:__ +
    +...
    +  
      +
    • + Search: +
    • +
    • + Sort by: + +
    • +
    + +
      +
    • + {{phone.name}} +

      {{phone.snippet}}

      +
    • +
    +... +
    + +__`app/js/controller.js`:__ +
    +/* App Controllers */
    +
    +function PhoneListCtrl() {
    +  this.phones = [{"name": "Nexus S",
    +                  "snippet": "Fast just got faster with Nexus S.",
    +                  "age": 0},
    +                 {"name": "Motorola XOOM™ with Wi-Fi",
    +                  "snippet": "The Next, Next Generation tablet.",
    +                  "age": 1},
    +                 {"name": "MOTOROLA XOOM™",
    +                  "snippet": "The Next, Next Generation tablet.",
    +                  "age": 2}];
    +
    +  this.orderProp = 'age';
    +}
    +
    + +__`test/unit/controllerSpec.js`:__ +
    +/* jasmine specs for controllers go here */
    +describe('PhoneCat controllers', function() {
    +
    +  describe('PhoneListCtrl', function(){
    +    var scope, $browser, ctrl;
    +
    +    beforeEach(function() {
    +      ctrl = new PhoneListCtrl();
    +    });
    +
    +
    +    it('should create "phones" model with 3 phones', function() {
    +      expect(ctrl.phones.length).toBe(3);
    +    });
    +
    +
    +    it('should set the default value of orderProp model', function() {
    +      expect(ctrl.orderProp).toBe('age');
    +    });
    +  });
    +});
    +
    + +__`test/e2e/scenarios.js`:__ +
    +/* jasmine-like end2end tests go here */
    +describe('PhoneCat App', function() {
    +
    +  describe('Phone list view', function() {
    +
    +    beforeEach(function() {
    +      browser().navigateTo('../../app/index.html');
    +    });
    +
    +
    +    it('should filter the phone list as user types into the search box', function() {
    +      expect(repeater('.phones li').count()).toBe(3);
    +
    +      input('query').enter('nexus');
    +      expect(repeater('.phones li').count()).toBe(1);
    +
    +      input('query').enter('motorola');
    +      expect(repeater('.phones li').count()).toBe(2);
    +    });
    +
    +
    +    it('should be possible to control phone order via the drop down select box', function() {
    +      input('query').enter('tablet'); //let's narrow the dataset to make the test assertions
    +      shorter
    +
    +      expect(repeater('.phones li', 'Phone List').column('a')).
    +          toEqual(["Motorola XOOM\u2122 with Wi-Fi",
    +                   "MOTOROLA XOOM\u2122"]);
    +
    +      select('orderProp').option('alphabetical');
    +
    +      expect(repeater('.phones li', 'Phone List').column('a')).
    +          toEqual(["MOTOROLA XOOM\u2122",
    +                   "Motorola XOOM\u2122 with Wi-Fi"]);
    +    });
    +  });
    +});
    +
    + +## Discussion: + +To provide dynamic ordering, we employ another one of angular's "array type augmenters" and let +the data binding do the rest of the work for us: + +* First, we provide a ` +
  • +
  • + Sort by: + +
  • + + + +... +
    + +__`app/js/controller.js`__ (Unchanged): +
    +/* App Controllers */
    +
    +function PhoneListCtrl($xhr) {
    +  var self = this;
    +
    +  $xhr('GET', 'phones/phones.json', function(code, response) {
    +    self.phones = response;
    +  });
    +
    +  self.orderProp = 'age';
    +}
    +
    +//PhoneListCtrl.$inject = ['$xhr'];
    +
    + +__`app/phones/phones.json`__ (sample snippet): +
    + [
    +  {
    +   "age": 4,
    +   ...
    +   "carrier": "T-Mobile",
    +   "id": "motorola-defy-with-motoblur",
    +   "imageUrl": "http://google.com/phone/image/small/640001",
    +   "name": "Motorola DEFY\u2122 with MOTOBLUR\u2122",
    +   "snippet": "Are you ready for everything life throws your way?"
    +  },
    +  …
    + ]
    +
    + +__`test/e2e/scenarios.js`__: +
    +...
    +    it('should render phone specific links', function() {
    +      input('query').enter('nexus');
    +      element('.phones li a').click();
    +      expect(browser().location().hash()).toBe('/phones/nexus-s');
    +    });
    +...
    +
    + +## Discussion: + +* Note that we're using {@link guide.expression angular expressions} enclosed in the now-familiar +{@link angular.markup double-curly brace markup} in the href attribute values. These represent +attribute bindings, and work the same way as the bindings we saw in previous steps. + +* Note also the use of the {@link angular.directive.ng:src ng:src} directive in the `` tag. +That directive prevents the browser from treating the angular `{{ exppression }}` markup +literally, as it would do if we tried to use markup in a regular `src` attribute. Use `ng:src` to +keep the browser from eagerly making an extra http request to an invalid location. + +* We expanded our end-to-end test to verify that the app is generating correct links to the phone +views we will implement in the upcoming steps. + + + + + + + + + +
    {@link tutorial.step_05 Previous}{@link http://angular.github.com/angular-phonecat/step-6/app Example}{@link tutorial Tutorial Home}{@link https://github.com/angular/angular-phonecat/compare/step-5...step-6 Code +Diff}{@link tutorial.step_07 Next}
    diff --git a/docs/content/tutorial/step_07.ngdoc b/docs/content/tutorial/step_07.ngdoc new file mode 100755 index 00000000..802130c5 --- /dev/null +++ b/docs/content/tutorial/step_07.ngdoc @@ -0,0 +1,181 @@ +@workInProgress +@ngdoc overview +@name Tutorial: Step 7 +@description + + + + + + + + +
    {@link tutorial.step_06 Previous}{@link http://angular.github.com/angular-phonecat/step-7/app Live Demo +}{@link tutorial Tutorial Home}{@link https://github.com/angular/angular-phonecat/compare/step-6...step-7 Code +Diff}{@link tutorial.step_08 Next}
    + +Our app is slowly growing and becoming more complex. Up until now, the app provided our users with +just one view (the list of all phones), and all of our template code was located in the +`index.html` file. The next step in building our app is the addition of a view that will show +detailed information about each of the devices in our list. + +To add the detailed view, we could expand the `index.html` file to contain template code for both +views, but that would get messy very quickly. Instead, we are going to turn the `index.html` +template into what we call a "layout template". This is a template that is common for all views in +our application. Other "partial templates" are then included into this layout template depending +on the current "route" — the view that is currently displayed to the user. + +Similarly as with templates, angular also allows for controllers and scopes managed by these +controllers to be nested. We are going to create a "root" controller called `PhoneCatCtrl`, which +will contain the declaration of routes for the application. + +Application routes in angular are declared via the {@link angular.service.$route $route} service. +This services 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, and Back and Forward browser navigation. + +We'll use the $route service to declare that our application consists of two different views: one +view presents the phone listing, and the other view presents the details for a particular phone. +Each view will have the template stored in a separate file in the `app/partials/` directory. +Similarly each view will have a controller associated with it. These will be stored in the +existing `app/js/controllers.js` file. + +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. + +For now we are going to get all the routing going, and move the phone listing template into a +separate file. We'll save the implementation of the phone details View for the next step. + +__`app/index.html`:__ +
    +...
    +
    +
    +  
    +
    +  
    +  
    +
    +
    +
    + +__`app/partials/phone-list.html`:__ +
    +
      +
    • + Search: +
    • +
    • + Sort by: + +
    • +
    + + +
    + +__`app/partials/phone-list.html`:__ +
    +TBD: detail view for {{params.phoneId}}
    +
    + +__`app/js/controller.js`:__ +
    +/* App Controllers */
    +
    +function PhoneCatCtrl($route) {
    +  var self = this;
    +
    +  $route.when('/phones',
    +              {template: 'partials/phone-list.html',   controller: PhoneListCtrl});
    +  $route.when('/phones/:phoneId',
    +              {template: 'partials/phone-detail.html', controller: PhoneDetailCtrl});
    +  $route.otherwise({redirectTo: '/phones'});
    +
    +  $route.onChange(function(){
    +    self.params = $route.current.params;
    +  });
    +
    +  $route.parent(this);
    +}
    +
    +//PhoneCatCtrl.$inject = ['$route'];
    +
    +
    +function PhoneListCtrl($xhr) {
    +  var self = this;
    +
    +  $xhr('GET', 'phones/phones.json', function(code, response) {
    +    self.phones = response;
    +  });
    +
    +  self.orderProp = 'age';
    +}
    +
    +//PhoneListCtrl.$inject = ['$xhr'];
    +
    +
    +function PhoneDetailCtrl() {}
    +
    + +## Discussion: + +* __The View.__ Our View template in `index.html` has been reduced down to this: +``. As described above, it is now a "layout template". We added the following +two new View templates: + + * `app/partials/phone-list.html` for the phone list. The phone-list view was formerly our + main view. We simply moved the code from `index.html` to here. + + * `app/partials/phone-detail.html` for the phone details (just a placeholder template for now). + +* __The Controller(s).__ We now have a new root controller (`PhoneCatCtrl`) and two +sub-controllers (`PhoneListCtrl` and `PhoneDetailCtrl`). These inherit the model properties and +behavior from the root controller. + + * __`$route.`__ The root controller's job now is to set up the `$route` configuration: + + * When the fragment part of the URL in the browser ends in "/phones", `$route` service + grabs the `phone-list.html` template, compiles it, and links it with a new scope that is + controlled by our `PhoneListCtrl` controller. + + * When the URL ends in "/phones/:phoneId", `$route` compiles and links the + `phone-detail.html` template as it did with `phone-list.html`. But note the use of the + `:phoneId` parameter declaration in the `path` argument of `$route.when()`: `$route` + services provides all the values for variables defined in this way as + `$route.current.params` map. In our route, `$route.current.params.phoneId` always holds + the current contents of the `:phoneId` portion of the URL. We will use the `phoneId` + parameter when we fetch the phone details in Step 8. + + * Any other URL fragment gets redirected to `/phones`. + + * __Controller/Scope inheritance.__ In the function passed into `$route`'s `onChange()` + method, we copied url parameters extracted from the current route to the `params` property in + the root scope. This property is inherited by child scopes created for our view controllers + and accessible by these controllers. + + * __Tests.__ To automatically verify that everything is wired properly, we write end to end + tests that navigate to various URLs and verify that the correct view was rendered. + + + + + + + + + +
    {@link tutorial.step_06 Previous}{@link http://angular.github.com/angular-phonecat/step-7/app Live Demo +}{@link tutorial Tutorial Home}{@link https://github.com/angular/angular-phonecat/compare/step-6...step-7 Code +Diff}{@link tutorial.step_08 Next}
    diff --git a/docs/content/tutorial/step_08.ngdoc b/docs/content/tutorial/step_08.ngdoc new file mode 100755 index 00000000..65ce6883 --- /dev/null +++ b/docs/content/tutorial/step_08.ngdoc @@ -0,0 +1,148 @@ +@workInProgress +@ngdoc overview +@name Tutorial: Step 8 +@description + + + + + + + + +
    {@link tutorial.step_07 Previous}{@link http://angular.github.com/angular-phonecat/step-8/app Live Demo +}{@link tutorial Tutorial Home}{@link https://github.com/angular/angular-phonecat/compare/step-7...step-8 Code +Diff}{@link tutorial.step_09 Next}
    + +In this step, we implement the Phone Details View template. Once again we will use {@link +angular.services.$xhr $xhr} to fetch our data, and we'll flesh out the `phone-details.html` View +template. + +__`app/partials/phone-details.html`:__ +
    +
    +
    +

    {{phone.name}}

    + +

    {{phone.description}}

    + +
      +
    • + +
    • +
    + +
      +
    • + Availability and Networks +
      +
      Availability
      +
      {{availability}}
      +
      +
    • + ... + + Additional Features +
      {{phone.additionalFeatures}}
      + +
    +
    + +__`app/js/controller.js`:__ +
    +function PhoneCatCtrl($route) (same as Step 7)
    +
    +function PhoneListCtrl($xhr) (same as Step 7)
    +
    +function PhoneDetailCtrl($xhr) {
    +  var self = this;
    +
    +  $xhr('GET', 'phones/' + self.params.phoneId + '.json', function(code, response) {
    +    self.phone = response;
    +  });
    +}
    +
    +//PhoneDetailCtrl.$inject = ['$xhr'];
    +
    + +__`app/phones/nexus-s.json`:__ (sample snippet) +
    +{
    +  "additionalFeatures": "Contour Display, Near Field Communications (NFC), Three-axis gyroscope,
    +  Anti-fingerprint display coating, Internet Calling support (VoIP/SIP)",
    +  "android": {
    +      "os": "Android 2.3",
    +      "ui": "Android"
    +  },
    +  ...
    +  "images": [
    +      "img/phones/nexus-s.0.jpg",
    +      "img/phones/nexus-s.1.jpg",
    +      "img/phones/nexus-s.2.jpg",
    +      "img/phones/nexus-s.3.jpg"
    +  ],
    +  "storage": {
    +      "flash": "16384MB",
    +      "ram": "512MB"
    +  }
    +}
    +
    + +__`test/unit/controllerSpec.js`:__ +
    +...
    +    it('should fetch phone detail', function(){
    +      scope.params = {phoneId:'xyz'};
    +      $browser.xhr.expectGET('phones/xyz.json').respond({name:'phone xyz'});
    +      ctrl = scope.$new(PhoneDetailCtrl);
    +
    +      expect(ctrl.phone).toBeUndefined();
    +      $browser.xhr.flush();
    +
    +      expect(ctrl.phone).toEqual({name:'phone xyz'});
    +    });
    +...
    +
    + +__`test/e2e/scenarios.js`:__ +
    +...
    +  describe('Phone detail view', function() {
    +
    +    beforeEach(function() {
    +      browser().navigateTo('../../app/index.html#/phones/nexus-s');
    +    });
    +
    +
    +    it('should display nexus-s page', function() {
    +      expect(binding('phone.name')).toBe('Nexus S');
    +    });
    +  });
    +...
    +
    + +## Discussion: + +* Phone Details View Template. There is nothing fancy or new here, just note where we use the +angular `{{ expression }}` markup and directives to project phone data from our model into the +view. + +* Note how we used the `$route` `params` object from the scope managed by the root controller +(`PhoneCatCtrl`), to construct the path for the phone details xhr request. The rest of this step +is simply applying the previously learned concepts and angular APIs to create a large template +that displays a lot of data about a phone. + +* Tests. We updated the existing end to end test and wrote a new unit test that is similar in +spirit to the one we wrote for the `PhoneListCtrl` controller. + + + + + + + + + +
    {@link tutorial.step_07 Previous}{@link http://angular.github.com/angular-phonecat/step-8/app Live Demo +}{@link tutorial Tutorial Home}{@link https://github.com/angular/angular-phonecat/compare/step-7...step-8 Code +Diff}{@link tutorial.step_09 Next}
    diff --git a/docs/content/tutorial/step_09.ngdoc b/docs/content/tutorial/step_09.ngdoc new file mode 100755 index 00000000..2d6ed925 --- /dev/null +++ b/docs/content/tutorial/step_09.ngdoc @@ -0,0 +1,108 @@ +@workInProgress +@ngdoc overview +@name Tutorial: Step 9 +@description + + + + + + + + +
    {@link tutorial.step_08 Previous}{@link http://angular.github.com/angular-phonecat/step-9/app Live Demo +}{@link tutorial Tutorial Home}{@link https://github.com/angular/angular-phonecat/compare/step-8...step-9 Code +Diff}{@link tutorial.step_10 Next}
    + +In this step, we have determined that the built-in angular display filters ({@link +angular.filter.number number}, {@link angular.filter.currency currency}, {@link +angular.filter.date date}, etc.) don't handle what we want to do, so we get to create our own +custom {@link angular.filter filter}. + +In the previous step, the details page displayed either "true" or "false" to indicate whether +certain phone features were present or not. Our custom "checkmark" filter replaces those text +strings with glyphs: ✓ for "true", and ✘ for "false". + +Our filter code lives in `app/js/filters.js`: + +__`app/index.html`:__ +
    +...
    + 
    + 
    + 
    +...
    +
    + +In the phone details template, we employ our filter for angular expressions whose values are +"true" or "false"; `{{ [phone_feature] | checkmark }}`: + +__`app/partials/phone-detail.html`:__ +
    +
    +

    {{phone.name}}

    +

    {{phone.description}}

    +... +
      + ... +
    • + Connectivity +
      +
      Network Support
      +
      {{phone.connectivity.cell}}
      +
      WiFi
      +
      {{phone.connectivity.wifi}}
      +
      Bluetooth
      +
      {{phone.connectivity.bluetooth}}
      +
      Infrared
      +
      {{phone.connectivity.infrared | checkmark}}
      +
      GPS
      +
      {{phone.connectivity.gps | checkmark}}
      +
      +
    • +... +
    +
    + +__`app/js/filters.js`:__ (New) +
    +angular.filter('checkmark', function(input) {
    +  return input ? '\u2713' : '\u2718';
    +});
    +
    + +__`test/unit/filtersSpec.js`:__ (New) +
    +describe('checkmark filter', function() {
    +
    +  it('should convert boolean values to unicode checkmark or cross', function() {
    +    expect(angular.filter.checkmark(true)).toBe('\u2713');
    +    expect(angular.filter.checkmark(false)).toBe('\u2718');
    +  });
    +})
    +
    + +## Discussion: + +* This example shows how easy it is to roll your own filters for displaying data. As explained in +the "Writing your own Filters" section of the {@link angular.filter angular.filter} page, you +simply register your custom filter function on to the `angular.filter` function. + +* In this example, our filter name is "checkmark"; our input is either "true" or "false", and we +return one of two unicode characters we have chosen to represent true or false (`\u2713` and +`\u2718`). + +* We created a new unit test to verify that our custom filter converts boolean values to unicode +characters. + + + + + + + + + +
    {@link tutorial.step_08 Previous}{@link http://angular.github.com/angular-phonecat/step-9/app Live Demo +}{@link tutorial Tutorial Home}{@link https://github.com/angular/angular-phonecat/compare/step-8...step-9 Code +Diff}{@link tutorial.step_10 Next}
    diff --git a/docs/content/tutorial/step_10.ngdoc b/docs/content/tutorial/step_10.ngdoc new file mode 100644 index 00000000..130b4023 --- /dev/null +++ b/docs/content/tutorial/step_10.ngdoc @@ -0,0 +1,110 @@ +@workInProgress +@ngdoc overview +@name Tutorial: Step 10 +@description + + + + + + + + +
    {@link tutorial.step_09 Previous}{@link http://angular.github.com/angular-phonecat/step-10/app Live Demo +}{@link tutorial Tutorial Home}{@link https://github.com/angular/angular-phonecat/compare/step-9...step-10 +Code Diff}{@link tutorial.step_11 Next}
    + +The phone details view displays one large image of the current phone and several smaller thumbnail +images. It would be great if we could replace the large image with any of the thumbnails just by +clicking on the desired thumbnail image. Let's have a look how we can do this with angular. + +__`app/partials/phone-detail.html`:__ +
    +
    +
    +

    {{phone.name}}

    + +

    {{phone.description}}

    + +
      +
    • + +
    • +
    +... +
    + +__`app/js/controllers.js`:__ +
    +...
    +function PhoneDetailCtrl($xhr) {
    +  var self = this;
    +
    +  $xhr('GET', 'phones/' + self.params.phoneId + '.json', function(code, response) {
    +    self.phone = response;
    +    self.mainImageUrl = response.images[0];
    +  });
    +
    +  self.setImage = function(imageUrl) {
    +    self.mainImageUrl = imageUrl;
    +  }
    +}
    +
    +//PhoneDetailCtrl.$inject = ['$xhr'];
    +
    + +__`test/e2e/scenarios.js`:__ +
    +/* jasmine-like end2end tests go here */
    +...
    +  describe('Phone detail view', function() {
    +
    +    beforeEach(function() {
    +      browser().navigateTo('../../app/index.html#/phones/nexus-s');
    +    });
    +
    +
    +    it('should display nexus-s page', function() {
    +      expect(binding('phone.name')).toBe('Nexus S');
    +    });
    +
    +    it('should display the first phone image as the main phone image', function() {
    +       expect(element('img.phone').attr('src')).toBe('img/phones/nexus-s.0.jpg');
    +    });
    +
    +
    +    it('should swap main image if a thumbnail image is clicked on', function() {
    +      element('.phone-thumbs li:nth-child(3) img').click();
    +      expect(element('img.phone').attr('src')).toBe('img/phones/nexus-s.2.jpg');
    +
    +      element('.phone-thumbs li:nth-child(1) img').click();
    +      expect(element('img.phone').attr('src')).toBe('img/phones/nexus-s.0.jpg');
    +    });
    +  });
    +});
    +
    + +## Discussion: + +Adding the phone image swapping feature is fairly straightforward: + +* We defined the `mainImageUrl` model property in the details controller (`PhoneDetailCtrl`) and +set the default value of `mainImageUrl` to the first image in the array of images. +* We created a `setImage` controller method to change `mainImageUrl` to the image clicked on by +the user. +* We registered an `{@link angular.directive.ng:click ng:click}` handler for thumb images to use +the `setImage` controller method. +* We expanded the end-to-end test to verify that our new feature is swapping images correctly. + + + + + + + + + + +
    {@link tutorial.step_09 Previous}{@link http://angular.github.com/angular-phonecat/step-10/app Live Demo +}{@link tutorial Tutorial Home}{@link https://github.com/angular/angular-phonecat/compare/step-9...step-10 +Code Diff}{@link tutorial.step_11 Next}
    diff --git a/docs/content/tutorial/step_11.ngdoc b/docs/content/tutorial/step_11.ngdoc new file mode 100644 index 00000000..e383f406 --- /dev/null +++ b/docs/content/tutorial/step_11.ngdoc @@ -0,0 +1,178 @@ +@workInProgress +@ngdoc overview +@name Tutorial: Step 11 +@description + + + + + + + + +
    {@link tutorial.step_10 Previous}{@link http://angular.github.com/angular-phonecat/step-11/app Live Demo +}{@link tutorial Tutorial Home}{@link https://github.com/angular/angular-phonecat/compare/step-10...step-11 +Code Diff}Next
    + +And so we arrive at the last step of this tutorial. Here we 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} APIs, HTTP methods and URLs. + +__`app/index.html`.__ +
    +...
    +  
    +...
    +
    + + +__`app/js/services.js`.__ (New) +
    + angular.service('Phone', function($resource){
    +  return $resource('phones/:phoneId.json', {}, {
    +    query: {method:'GET', params:{phoneId:'phones'}, isArray:true}
    +  });
    + });
    +
    + +__`app/js/controllers.js`.__ +
    +...
    +
    +function PhoneListCtrl(Phone_) {
    +  this.orderProp = 'age';
    +  this.phones = Phone_.query();
    +}
    +//PhoneListCtrl.$inject = ['Phone'];
    +
    +
    +function PhoneDetailCtrl(Phone_) {
    +  this.phone = Phone_.get({phoneId:this.params.phoneId});
    +}
    +//PhoneDetailCtrl.$inject = ['Phone'];
    +
    + +__`test/unit/controllersSpec.js`:__ +
    +/* jasmine specs for controllers go here */
    +describe('PhoneCat controllers', function() {
    +
    +  beforeEach(function(){
    +    this.addMatchers({
    +      toEqualData: function(expected) {
    +        return angular.equals(this.actual, expected);
    +      }
    +    });
    +  });
    +
    +  describe('PhoneListCtrl', function(){
    +    var scope, $browser, ctrl;
    +
    +    beforeEach(function() {
    +      scope = angular.scope();
    +      $browser = scope.$service('$browser');
    +
    +      $browser.xhr.expectGET('phones/phones.json').respond([{name: 'Nexus S'},
    +                                                            {name: 'Motorola DROID'}]);
    +      ctrl = scope.$new(PhoneListCtrl);
    +    });
    +
    +    it('should create "phones" model with 2 phones fetched from xhr', function() {
    +      expect(ctrl.phones).toEqual([]);
    +      $browser.xhr.flush();
    +
    +      expect(ctrl.phones).toEqualData([{name: 'Nexus S'},
    +                                       {name: 'Motorola DROID'}]);
    +    });
    +
    +    it('should set the default value of orderProp model', function() {
    +      expect(ctrl.orderProp).toBe('age');
    +    });
    +  });
    +
    +
    +  describe('PhoneDetailCtrl', function(){
    +    var scope, $browser, ctrl;
    +
    +    beforeEach(function() {
    +      scope = angular.scope();
    +      $browser = scope.$service('$browser');
    +    });
    +
    +    beforeEach(function() {
    +      scope = angular.scope();
    +      $browser = scope.$service('$browser');
    +    });
    +
    +    it('should fetch phone detail', function(){
    +      scope.params = {phoneId:'xyz'};
    +      $browser.xhr.expectGET('phones/xyz.json').respond({name:'phone xyz'});
    +      ctrl = scope.$new(PhoneDetailCtrl);
    +
    +      expect(ctrl.phone).toEqualData({});
    +      $browser.xhr.flush();
    +
    +      expect(ctrl.phone).toEqualData({name:'phone xyz'});
    +    });
    +  });
    +});
    +
    + + +## Discussion: + +* 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. + + An important thing to notice in our controller code is that we don't pass any callback + functions when invoking methods of our Phone services. It looks as if the result were returned + synchronously. That is not the case at all. What is returned synchronously is a "future" — an + object, which will be filled with data when the xhr response returns. Because of the + data-binding in angular, we can use this future and bind it to our template. Then, when the + data arrives, the view will automatically update. See? Angular tries hard to make simple + stuff simple. + +* Once again we make use of `$route's` params, this time to construct the URL passed as a +parameter to `$resource` in our `services.js` script. + +* Last, but certainly not least, we expanded and modified our unit test to verify that our new +service is returning data as we expect it to. + + In our assertions we use a newly-defined `toEqualData` {@link + http://pivotal.github.com/jasmine/jsdoc/symbols/jasmine.Matchers.html Jasmine matcher}, which + compares only object properties and ignores methods. This is necessary, because the `$resource` + client will augment the response object with handy methods for updating and deleting the + resource (we don't use these in our tutorial though). + +There you have it! We have created a web app in a relatively short amount of time. + +## Closing Notes: + +* 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 sample code, see the {@link cookbook Cookbook}. + +* When you are ready to start developing a project using angular, be sure to begin with the {@link +https://github.com/angular/angular-seed angular seed app}. + +* We hope this tutorial was useful to you, and that you learned enough about angular to make you +want to learn more. Of course, we especially hope you are inspired to go out and develop angular +web apps of your own, and perhaps you might even be interested in {@link contribute contributing} +to angular. + + + + + + + + + +
    {@link tutorial.step_10 Previous}{@link http://angular.github.com/angular-phonecat/step-11/app Live Demo +}{@link tutorial Tutorial Home}{@link https://github.com/angular/angular-phonecat/compare/step-10...step-11 +Code Diff}Next
    -- cgit v1.2.3