diff options
Diffstat (limited to 'docs/content')
45 files changed, 4840 insertions, 0 deletions
| 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. + +<pre> +angular.attrMarkup('extraClass', function(attrValue, attrName, element){ +  if (attrName == 'additional-class') { +    element.addClass(attrValue); +  } +}); +</pre> 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 +`<span ng:bind="1+2"></span>`. How does this work? The compiler passes the attribute value +`1+2` to the ng:bind extension, which in turn tells the {@link angular.scope} to watch that +expression and report changes. On any change it sets the span text to the expression value. + +Here's how to define {@link angular.directive.ng:bind ng:bind}: +<pre> +  angular.directive('ng:bind', function(expression, compiledElement) { +    var compiler = this; +    return function(linkElement) { +      var currentScope = this; +      currentScope.$watch(expression, function(value) { +        linkElement.text(value); +      }); +    }; +  }); +</pre> + +# Directive vs. Attribute Widget +Both [attribute widgets](#!angular.widget) and directives can compile a DOM element +attribute. So why have two different ways to do the same thing? The answer is that order +matters, but we have no control over the order in which attributes are read. To solve this +we apply attribute widget before the directive. + +For example, consider this piece of HTML, which uses the directives `ng:repeat`, `ng:init`, +and `ng:bind`: +<pre> +  <ul ng:init="people=['mike', 'mary']"> +    <li ng:repeat="person in people" ng:init="a=a+1" ng:bind="person"></li> +  </ul> +</pre> + +Notice that the order of execution matters here. We need to execute +{@link angular.directive.ng:repeat ng:repeat} before we run the +{@link angular.directive.ng:init ng:init} and `ng:bind` on the `<li/>;`. This is because we +want to run the `ng:init="a=a+1` and `ng:bind="person"` once for each person in people. We +could not have used directive to create this template because attributes are read in an +unspecified order and there is no way of guaranteeing that the repeater attribute would +execute first. Using the `ng:repeat` attribute directive ensures that we can transform the +DOM element into a template. + +Widgets run before directives. Widgets may manipulate the DOM whereas directives are not +expected to do so, and so they run last. 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). + +<doc:example> + <doc:source> +   <script type="text/javascript"> +     angular.filter('reverse', function(input, uppercase, color) { +       var out = ""; +       for (var i = 0; i < input.length; i++) { +         out = input.charAt(i) + out; +       } +       if (uppercase) { +         out = out.toUpperCase(); +       } +       if (color) { +         this.$element.css('color', color); +       } +       return out; +     }); +   </script> + +   <input name="text" type="text" value="hello" /><br> +   No filter: {{text}}<br> +   Reverse: {{text|reverse}}<br> +   Reverse + uppercase: {{text|reverse:true}}<br> +   Reverse + uppercase + blue:  {{text|reverse:true:"blue"}} + </doc:source> + <doc:scenario> +  it('should reverse text', function(){ +    expect(binding('text|reverse')).toEqual('olleh'); +    input('text').enter('ABC'); +    expect(binding('text|reverse')).toEqual('CBA'); +  }); + </doc:scenario> +</doc:example> + + 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. + +<pre> +function reverse(text) { +  var reversed = []; +  for (var i = 0; i < text.length; i++) { +    reversed.unshift(text.charAt(i)); +  } +  return reversed.join(''); +} + +angular.formatter('reverse', { +  parse: function(value){ +    return reverse(value||'').toUpperCase(); +  }, +  format: function(value){ +    return reverse(value||'').toLowerCase(); +  } +}); +</pre> + +@example +<doc:example> + <doc:source> +  <script type="text/javascript"> +  function reverse(text) { +    var reversed = []; +    for (var i = 0; i < text.length; i++) { +      reversed.unshift(text.charAt(i)); +    } +    return reversed.join(''); +  } + +  angular.formatter('reverse', { +    parse: function(value){ +      return reverse(value||'').toUpperCase(); +    }, +    format: function(value){ +      return reverse(value||'').toLowerCase(); +    } +  }); +  </script> + +  Formatted: +  <input type="text" name="data" value="angular" ng:format="reverse"/> +  <br/> + +  Stored: +  <input type="text" name="data"/><br/> +  <pre>{{data}}</pre> + </doc:source> + <doc:scenario> +  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'); +  }); + </doc:scenario> +</doc:example> + 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 + +<pre> +{{expression}} +</pre> + +to: + +<pre> +<span ng:bind="expression"></span> +</pre> + +For example `{{1+2}}` is easier to write and understand than `<span ng:bind="1+2"></span>`. 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 `<hr/>`. + +In other words, this HTML: +<pre> +header +--- +footer +</pre> + +should translate to: +<pre> +header +<hr/> +footer +</pre> + +Here's how the angular compiler could be extended to achieve this: +<pre> +angular.markup('---', function(text, textNode, parentElement) { +  var compiler = this; +  var index = text.indexOf('---'); +  if (index > -1) { +    var before = compiler.text(text.substring(0, index)); +    var hr = compiler.element('hr'); +    var after = compiler.text(text.substring(index + 3)); +    textNode.after(after); +    textNode.after(hr); +    textNode.after(before); +    textNode.remove(); +  } +}); +</pre> + +Unlike {@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.<string>} - Array of service ids that this service depends on. These +      services will be passed as arguments into the factory function in the same order as specified +      in the `$inject` array. Defaults to `[]`. +    - `$eager` - {boolean} - If true, the service factory will be called and thus, the service will +      be instantiated when angular boots. If false, service will be lazily instantiated when it is +      first requested during instantiation of a dependant. Defaults to `false`. + +The `this` of the factory function is bound to the root scope of the angular application. + +angular enables services to participate in dependency injection (DI) by registering themselves with +angular's DI system (injector) under a `name` (id) as well as by declaring dependencies which need +to be provided for the factory function of the registered service. The ability to swap dependencies +for mocks/stubs/dummies in tests allows for services to be highly testable. + +Here is an example of very simple service. This service requires $window service (it's +passed as a parameter to factory function) and it's just a function. + +This service simple stores all notifications and after third one, it displays all of them by +window alert. +<pre> +       angular.service('notify', function(win) { +         var msgs = []; +         return function(msg) { +           msgs.push(msg); +           if (msgs.length == 3) { +             win.alert(msgs.join("\n")); +             msgs = []; +           } +         }; +       }, {$inject: ['$window']}); +</pre> + +And here is a unit test for this service. We use Jasmine spy (mock) instead of real browser's alert. +<pre> +var mock, notify; + +beforeEach(function() { +  mock = {alert: jasmine.createSpy()}; +  notify = angular.service('notify')(mock); +}); + +it('should not alert first two notifications', function() { +  notify('one'); +  notify('two'); +  expect(mock.alert).not.toHaveBeenCalled(); +}); + +it('should alert all after third notification', function() { +  notify('one'); +  notify('two'); +  notify('three'); +  expect(mock.alert).toHaveBeenCalledWith("one\ntwo\nthree"); +}); + +it('should clear messages after alert', function() { +  notify('one'); +  notify('two'); +  notify('third'); +  notify('more'); +  notify('two'); +  notify('third'); +  expect(mock.alert.callCount).toEqual(2); +  expect(mock.alert.mostRecentCall.args).toEqual(["more\ntwo\nthird"]); +}); +</pre> + +# Injecting services into controllers +Using services as dependencies for controllers is very similar to using them as dependencies for +another service. + +JavaScript is dynamic language, so DI is not able to figure out which services to inject by +static types (like in static typed languages). Therefore you must specify the service name +by the `$inject` property - it's an array that contains strings with names of services to be +injected. The name must match the id that service has been registered as with angular. +The order of the services in the array matters, because this order will be used when calling +the factory function with injected parameters. The names of parameters in factory function +don't matter, but by convention they match the service ids. +<pre> +function myController($loc, $log) { +  this.firstMethod = function() { +    // use $location service +    $loc.setHash(); +  }; +  this.secondMethod = function() { +    // use $log service +    $log.info('...'); +  }; +} +// which services to inject ? +myController.$inject = ['$location', '$log']; +</pre> + +@example +<doc:example> + <doc:source> +  <script type="text/javascript"> +   angular.service('notify', function(win) { +     var msgs = []; +     return function(msg) { +       msgs.push(msg); +       if (msgs.length == 3) { +         win.alert(msgs.join("\n")); +         msgs = []; +       } +     }; +   }, {$inject: ['$window']}); + +   function myController(notifyService) { +     this.callNotify = function(msg) { +       notifyService(msg); +     }; +   } + +   myController.$inject = ['notify']; +  </script> + +  <div ng:controller="myController"> +  <p>Let's try this simple notify service, injected into the controller...</p> +  <input ng:init="message='test'" type="text" name="message" /> +  <button ng:click="callNotify(message);">NOTIFY</button> +  </div> + </doc:source> + <doc:scenario> +   it('should test service', function(){ +     expect(element(':input[name=message]').val()).toEqual('test'); +   }); + </doc:scenario> +</doc:example> 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. + +<doc:example> +  <doc:source> +    Change me: <input type="text" name="number" ng:validate="integer" value="123"> +  </doc:source> +  <doc:scenario> +    it('should validate the default number string', function() { +      expect(element('input[name=number]').attr('class')). +         not().toMatch(/ng-validation-error/); +    }); +    it('should not validate "foo"', function() { +      input('number').enter('foo'); +      expect(element('input[name=number]').attr('class')). +         toMatch(/ng-validation-error/); +    }); +  </doc:scenario> +</doc:example> + + +# Writing your own Validators +Writing your own validator is easy. To make a function available as a +validator, just define the JavaScript function on the `angular.validator` +object. <angular/> passes in the input to validate as the first argument +to your function. Any additional validator arguments are passed in as +additional arguments to your function. + +You can use these variables in the function: + +* `this` — The current scope. +* `this.$element` — The DOM element containing the binding. This allows the filter to manipulate +  the DOM in addition to transforming the input. + +In this example we have written a upsTrackingNo validator. +It marks the input text "valid" only when the user enters a well-formed +UPS tracking number. + +@css ng-validation-error +  When validation fails, this css class is applied to the binding, making its borders red by +  default. + +@example +<doc:example> + <doc:source> +  <script> +   angular.validator('upsTrackingNo', function(input, format) { +     var regexp = new RegExp("^" + format.replace(/9/g, '\\d') + "$"); +     return input.match(regexp)?"":"The format must match " + format; +   }); +  </script> +  <input type="text" name="trackNo" size="40" +        ng:validate="upsTrackingNo:'1Z 999 999 99 9999 999 9'" +        value="1Z 123 456 78 9012 345 6"/> + </doc:source> + <doc:scenario> +  it('should validate correct UPS tracking number', function() { +    expect(element('input[name=trackNo]').attr('class')). +       not().toMatch(/ng-validation-error/); +  }); + +  it('should not validate in correct UPS tracking number', function() { +    input('trackNo').enter('foo'); +    expect(element('input[name=trackNo]').attr('class')). +       toMatch(/ng-validation-error/); +  }); + </doc:scenario> +</doc:example> 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. + +<pre> +<my:watch exp="name"/> +</pre> + +You can implement `my:watch` like this: +<pre> +angular.widget('my:watch', function(compileElement) { +  var compiler = this; +  var exp = compileElement.attr('exp'); +  return function(linkElement) { +    var currentScope = this; +    currentScope.$watch(exp, function(value){ +      alert(value); +    }); +  }; +}); +</pre> + +# Attribute Widget +Let's implement the same widget, but this time as an attribute +that can be added to any existing DOM element. +<pre> +<div my:watch="name">text</div> +</pre> +You can implement `my:watch` attribute like this: +<pre> +angular.widget('@my:watch', function(expression, compileElement) { +  var compiler = this; +  return function(linkElement) { +    var currentScope = this; +    currentScope.$watch(expression, function(value){ +      alert(value); +    }); +  }; +}); +</pre> + +@example +<doc:example> + <doc:source> +  <script> +    angular.widget('my:time', function(compileElement){ +      compileElement.css('display', 'block'); +      return function(linkElement){ +        function update(){ +          linkElement.text('Current time is: ' + new Date()); +          setTimeout(update, 1000); +        } +        update(); +      }; +    }); +  </script> +  <my:time></my:time> + </doc:source> + <doc:scenario> + </doc:scenario> +</doc:example> 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. + +<doc:example> + <doc:source> +    <script> +    BuzzController.$inject = ['$resource']; +    function BuzzController($resource){ +     this.Activity = $resource( +      'https://www.googleapis.com/buzz/v1/activities/:userId/:visibility/:activityId/:comments', +      {alt:'json', callback:'JSON_CALLBACK'}, +      { get:     {method:'JSON', params:{visibility:'@self'}}, +       replies: {method:'JSON', params:{visibility:'@self', comments:'@comments'}} +      }); +    } +    BuzzController.prototype = { +     fetch: function(){ +      this.activities = this.Activity.get({userId:this.userId}); +     }, +     expandReplies: function(activity) { +      activity.replies = +       this.Activity.replies({userId:this.userId, activityId:activity.id}); +     } +    }; +    </script> +    <div ng:controller="BuzzController"> +     <input name="userId" value="googlebuzz"/> +     <button ng:click="fetch()">fetch</button> +     <hr/> +     <div class="buzz" ng:repeat="item in activities.data.items"> +      <h1 style="font-size: 15px;"> +       <img src="{{item.actor.thumbnailUrl}}" style="max-height:30px;max-width:30px;"/> +       <a href="{{item.actor.profileUrl}}">{{item.actor.name}}</a> +       <a href="" ng:click="expandReplies(item)" style="float: right;"> +        Expand replies: {{item.links.replies[0].count}} +       </a> +      </h1> +      {{item.object.content | html}} +      <div class="reply" ng:repeat="reply in item.replies.data.items" style="margin-left: 20px;"> +       <img src="{{reply.actor.thumbnailUrl}}" style="max-height:30px;max-width:30px;"/> +       <a href="{{reply.actor.profileUrl}}">{{reply.actor.name}}</a>: +       {{reply.content | html}} +      </div> +     </div> +    </div> + </doc:source> + <doc:scenario> +  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); +  }); + </doc:scenario> +</doc:example> 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 <angular/> does not force you to deal with bookmarks in any particular way, it has services +which make the common case described here very easy to implement. + +# Assumptions + +Your application consists of a single HTML page which bootstraps the application. We will refer +to this page as the chrome. +Your application is divided into several screens (or views) which the user can visit. For example, +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} + + +<doc:example> + <doc:source> +    <script> +     AppCntl.$inject = ['$route'] +     function AppCntl($route) { +      // define routes +      $route.when("",          {template:'./examples/welcome.html',  controller:WelcomeCntl}); +      $route.when("/settings", {template:'./examples/settings.html', controller:SettingsCntl}); +      $route.parent(this); + +      // initialize the model to something useful +      this.person = { +       name:'anonymous', +       contacts:[{type:'email', url:'anonymous@example.com'}] +      }; +     } + +     function WelcomeCntl($route){} +     WelcomeCntl.prototype = { +      greet: function(){ +       alert("Hello " + this.person.name); +      } +     }; + +     function SettingsCntl(){ +      this.cancel(); +     } +     SettingsCntl.prototype = { +      cancel: function(){ +       this.form = angular.copy(this.person); +      }, + +      save: function(){ +       angular.copy(this.form, this.person); +       window.location.hash = "#"; +      } +     }; +    </script> +    <div ng:controller="AppCntl"> +      <h1>Your App Chrome</h1> +      [ <a href="#">Welcome</a> | <a href="#/settings">Settings</a> ] +      <hr/> +      <span style="background-color: blue; color: white; padding: 3px;"> +        Partial: {{$route.current.template}} +      </span> +      <ng:view style="border: 1px solid blue; margin: 0; display:block; padding:1em;"></ng:view> +      <small>Your app footer </small> +    </div> + </doc:source> + <doc:scenario> +   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/); +   }); + </doc:scenario> +</doc:example> + + + +# 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. + + +<doc:example> + <doc:source> +  <script> +    function FormController(){ +      this.user = { +        name: 'John Smith', +        address:{line1: '123 Main St.', city:'Anytown', state:'AA', zip:'12345'}, +        contacts:[{type:'phone', value:'1(234) 555-1212'}] +      }; +      this.state = /^\w\w$/; +      this.zip = /^\d\d\d\d\d$/; +    } +  </script> +  <div ng:controller="FormController" class="example"> + +    <label>Name:</label><br/> +    <input type="text" name="user.name" ng:required/> <br/><br/> + +    <label>Address:</label><br/> +    <input type="text" name="user.address.line1" size="33" ng:required/> <br/> +    <input type="text" name="user.address.city" size="12" ng:required/>, +    <input type="text" name="user.address.state" size="2" ng:required ng:validate="regexp:state"/> +    <input type="text" name="user.address.zip" size="5" ng:required ng:validate="regexp:zip"/><br/><br/> + +    <label>Phone:</label> +    [ <a href="" ng:click="user.contacts.$add()">add</a> ] +    <div ng:repeat="contact in user.contacts"> +      <select name="contact.type"> +        <option>email</option> +        <option>phone</option> +        <option>pager</option> +        <option>IM</option> +      </select> +      <input type="text" name="contact.value" ng:required/> +       [ <a href="" ng:click="user.contacts.$remove(contact)">X</a> ] +    </div> +    <hr/> +    Debug View: +    <pre>user={{user}}</pre> +  </div> + + </doc:source> + <doc:scenario> +  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/); +  }); + </doc:scenario> +</doc:example> + + +# 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. + +<doc:example> + <doc:source> +    <script> +    UserForm.$inject = ['$invalidWidgets']; +    function UserForm($invalidWidgets){ +      this.$invalidWidgets = $invalidWidgets; +      this.state = /^\w\w$/; +      this.zip = /^\d\d\d\d\d$/; +      this.master = { +        name: 'John Smith', +        address:{ +          line1: '123 Main St.', +          city:'Anytown', +          state:'AA', +          zip:'12345' +        }, +        contacts:[ +          {type:'phone', value:'1(234) 555-1212'} +        ] +      }; +      this.cancel(); +    } + +    UserForm.prototype = { +      cancel: function(){ +        this.form = angular.copy(this.master); +      }, + +      save: function(){ +        this.master = this.form; +        this.cancel(); +      } +    }; +    </script> +    <div ng:controller="UserForm"> + +      <label>Name:</label><br/> +      <input type="text" name="form.name" ng:required/> <br/><br/> + +      <label>Address:</label><br/> +      <input type="text" name="form.address.line1" size="33" ng:required/> <br/> +      <input type="text" name="form.address.city" size="12" ng:required/>, +      <input type="text" name="form.address.state" size="2" ng:required ng:validate="regexp:state"/> +      <input type="text" name="form.address.zip" size="5" ng:required ng:validate="regexp:zip"/><br/><br/> + +      <label>Phone:</label> +      [ <a href="" ng:click="form.contacts.$add()">add</a> ] +      <div ng:repeat="contact in form.contacts"> +        <select name="contact.type"> +          <option>email</option> +          <option>phone</option> +          <option>pager</option> +          <option>IM</option> +        </select> +        <input type="text" name="contact.value" ng:required/> +         [ <a href="" ng:click="form.contacts.$remove(contact)">X</a> ] +      </div> +    <button ng:click="cancel()" disabled="{{master.$equals(form)}}">Cancel</button> +    <button ng:click="save()" disabled="{{$invalidWidgets.visible() || master.$equals(form)}}">Save</button> + +    <hr/> +    Debug View: +    <pre>form={{form}} +    master={{master}}</pre> +    </div> + </doc:source> + <doc:scenario> +  it('should enable save button', function(){ +    expect(element(':button:contains(Save)').attr('disabled')).toBeTruthy(); +    input('form.name').enter(''); +    expect(element(':button:contains(Save)').attr('disabled')).toBeTruthy(); +    input('form.name').enter('change'); +    expect(element(':button:contains(Save)').attr('disabled')).toBeFalsy(); +    element(':button:contains(Save)').click(); +    expect(element(':button:contains(Save)').attr('disabled')).toBeTruthy(); +  }); +  it('should enable cancel button', function(){ +    expect(element(':button:contains(Cancel)').attr('disabled')).toBeTruthy(); +    input('form.name').enter('change'); +    expect(element(':button:contains(Cancel)').attr('disabled')).toBeFalsy(); +    element(':button:contains(Cancel)').click(); +    expect(element(':button:contains(Cancel)').attr('disabled')).toBeTruthy(); +    expect(element(':input[name=form.name]').val()).toEqual('John Smith'); +  }); + </doc:scenario> +</doc:example> + + +#Things to notice + +* Cancel & save buttons are only enabled if the form is dirty -- there is something to cancel or +  save. +* Save button is only enabled if there are no validation errors on the form. +* Cancel reverts the form changes back to original state. +* Save updates the internal model of the form. +* Debug view shows the two models. One presented to the user form and the other being the pristine +  copy master. 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 + +<doc:example> + <doc:source> +  Your name: <input type="text" name="name" value="World"/> +  <hr/> +  Hello {{name}}! + </doc:source> + <doc:scenario> +   it('should change the binding when user enters text', function(){ +     expect(binding('name')).toEqual('World'); +     input('name').enter('angular'); +     expect(binding('name')).toEqual('angular'); +   }); + </doc:scenario> +</doc:example> + +# Things to notice + +Take a look through the source and note: + +* The script tag that {@link guide.bootstrap bootstraps} the angular environment. +* The text {@link angular.widget.HTML input widget} which is bound to the greeting name text. +* 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. + + +<doc:example> + <doc:source> +    <script> +    function TicTacToeCntl(){ +      this.cellStyle= { +        'height': '20px', +        'width': '20px', +        'border': '1px solid black', +        'text-align': 'center', +        'vertical-align': 'middle', +        'cursor': 'pointer' +      }; +      this.reset(); +      this.$watch('$location.hashSearch.board', this.readUrl); +    } +    TicTacToeCntl.prototype = { +      dropPiece: function(row, col) { +        if (!this.winner && !this.board[row][col]) { +          this.board[row][col] = this.nextMove; +          this.nextMove = this.nextMove == 'X' ? 'O' : 'X'; +          this.setUrl(); +        } +      }, +      reset: function(){ +        this.board = [ +          ['', '', ''], +          ['', '', ''], +          ['', '', ''] +        ]; +        this.nextMove = 'X'; +        this.winner = ''; +        this.setUrl(); +      }, +      grade: function(){ +       var b = this.board; +       this.winner = +        row(0) || row(1) || row(2) || +        col(0) || col(1) || col(2) || +        diagonal(-1) || diagonal(1); +       function row(r) { return same(b[r][0], b[r][1], b[r][2]);} +       function col(c) { return same(b[0][c], b[1][c], b[2][c]);} +       function diagonal(i) { return same(b[0][1-i], b[1][1], b[2][1+i]);} +       function same(a, b, c) { return (a==b && b==c) ? a : '';}; +      }, +      setUrl: function(){ +        var rows = []; +        angular.forEach(this.board, function(row){ +          rows.push(row.join(',')); +        }); +        this.$location.hashSearch.board = rows.join(';') + '/' + this.nextMove; +      }, +      readUrl: function(value) { +        if (value) { +          value = value.split('/'); +          this.nextMove = value[1]; +          angular.forEach(value[0].split(';'), function(row, i){ +            this.board[i] = row.split(','); +          }, this); +          this.grade(); +        } else { +          this.reset(); +        } +      } +    }; +    </script> +    <h3>Tic-Tac-Toe</h3> +    <div ng:controller="TicTacToeCntl"> +    Next Player: {{nextMove}} +    <div class="winner" ng:show="winner">Player {{winner}} has won!</div> +    <table class="board"> +      <tr ng:repeat="row in board" style="height:15px;"> +        <td ng:repeat="cell in row" ng:style="cellStyle" +                ng:click="dropPiece($parent.$index, $index)">{{cell}}</td> +      </tr> +    </table> +    <button ng:click="reset()">reset board</button> +    </div> + </doc:source> + <doc:scenario> +   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(); +   } + </doc:scenario> +</doc:example> + + +# Things to notice + +* The controller is defined in JavaScript and has no reference to the rendering logic. +* The controller is instantiated by <angular/> and injected into the view. +* The controller can be instantiated in isolation (without a view) and the code will still execute. +  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: + +<doc:example> + <doc:source> +  Hello {{'World'}}! + </doc:source> +</doc:example> + +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. + +<pre> +<!DOCTYPE HTML> +<html xmlns:ng="http://angularjs.org"> + <script type="text/javascript" src="http://code.angularjs.org/angular-0.0.0.min.js"></script> + <script type="text/javascript"> +  (function(window, previousOnLoad){ +   window.onload = function(){ +   try { (previousOnLoad||angular.noop)(); } catch(e) {} +   angular.compile(window.document)(); +   }; +  })(window, window.onload); + </script> + <body> +  Hello {{'World'}}! + </body> +</html> +</pre> + +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. + +<pre> +<html  xmlns:ng="http://angularjs.org"> +</pre> + + +# 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: + +<pre> +<html  xmlns:my="http://mydomain.com"> +</pre> + + +# 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 + +<img class="right" src="img/One_Way_Data_Binding.png"/> +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 +<img class="right" src="img/Two_Way_Data_Binding.png"/> +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 +<doc:example> + <doc:source> +  1+2={{1+2}} + </doc:source> + <doc:scenario> +  it('should calculate expression in binding', function(){ +    expect(binding('1+2')).toEqual('3'); +  }); + </doc:scenario> +</doc:example> + +You can try evaluating different expressions here: + +<doc:example> + <doc:source> +  <div ng:init="exprs=[]" class="expressions"> +    Expression: +    <input type='text' name="expr" value="3*10|currency" size="80"/> +    <button ng:click="exprs.$add(expr)">Evaluate</button> +    <ul> +     <li ng:repeat="expr in exprs"> +       [ <a href="" ng:click="exprs.$remove(expr)">X</a> ] +       <tt>{{expr}}</tt> => <span ng:bind="$parent.$eval(expr)"></span> +      </li> +    </ul> +  </div> + </doc:source> + <doc:scenario> +  it('should allow user expression testing', function(){ +     element('.expressions :button').click(); +     var li = using('.expressions ul').repeater('li'); +     expect(li.count()).toBe(1); +     expect(li.row(0)).toEqual(["3*10|currency", "$30.00"]); +  }); + </doc:scenario> +</doc:example> + +# Attribute Evaluation + +Evaluation of all attributes are against the current scope. Unlike JavaScript, where names +default to global window properties, angular expressions have to use $window to refer to the +global object. E.g. if you want to call alert(), which is defined on window, an expression must +use $window.alert(). This is done intentionally to prevent accidental access to the global state +(a common source of subtle bugs). + +<doc:example> + <doc:source> +  <div class="example2" ng:init="$window = $service('$window')"> +    Name: <input name="name" type="text" value="World"/> +    <button ng:click="($window.mockWindow || $window).alert('Hello ' + name)">Greet</button> +  </div> + </doc:source> + <doc:scenario> +  it('should calculate expression in binding', function(){ +    var alertText; +    this.addFutureAction('set mock', function($window, $document, done) { +      $window.mockWindow = { +        alert: function(text){ alertText = text; } +      }; +      done(); +    }); +    element(':button:contains(Greet)').click(); +    expect(this.addFuture('alert text', function(done) { +      done(null, alertText); +    })).toBe('Hello World'); +  }); + </doc:scenario> +</doc:example> + +## Forgiving + +Expression evaluation is forgiving to undefined and null. In JavaScript, evaluating a.b.c throws +an exception if a is not an object. While this makes sense for a general purpose language, the +expression evaluations are primarily used for data binding, which often look like this: `{{a.b.c}}`. +It makes more sense to show nothing than to throw an exception if a is undefined (e.g. perhaps +we are waiting for the server response, and it will become defined soon). If expression +evaluation wasn't forgiving we'd have to write bindings that clutter the code, for example: +`{{((a||{}).b||{}).c}}` + +Similarly, invoking a function a.b.c() on undefined or null simply returns undefined. + +Assignments work the same way in reverse. a.b.c = 10 creates the intermediary objects even if a +is undefined. + + +## No Control Flow Statements + +You cannot write a control flow statement in an expression. The reason behind this is core to +the angular philosophy that application logic should be in controllers, not in the view. If you +need a conditional (including ternary operators), loop, or to throw from a view expression, +delegate to a JavaScript method instead. + + +## Type Augmentation + +Built-in types have methods like [].push(), but the richness of these methods is limited. Consider +the example below, which allows you to do a simple search over a canned set of contacts. The +example would be much more complicated if we did not have the Array:$filter(). There is no +built-in method on Array called $filter and angular doesn't add it to Array.prototype because that +could collide with other JavaScript frameworks. + +For this reason the scope expression evaluator augments the built-in types to make them act like +they have extra methods. The actual method for $filter() is angular.Array.filter(). You can call +it from JavaScript. + +Extensions: You can further extend the expression vocabulary by adding new methods to +`angular.Array` or `angular.String`, etc. + +<doc:example> + <doc:source> +  <div ng:init="friends = [ +    {name:'John', phone:'555-1212'}, +    {name:'Mary', phone:'555-9876'}, +    {name:'Mike', phone:'555-4321'}, +    {name:'Adam', phone:'555-5678'}, +    {name:'Julie', phone:'555-8765'}]"></div> +  Search: <input name="searchText"/> +  <table class="example3"> +    <tr><th>Name</th><th>Phone</th><tr> +    <tr ng:repeat="friend in friends.$filter(searchText)"> +      <td>{{friend.name}}</td> +      <td>{{friend.phone}}</td> +    </tr> +  </table> + </doc:source> + <doc:scenario> +  it('should filter the list', function(){ +     var tr = using('table.example3').repeater('tr.ng-attr-widget'); +     expect(tr.count()).toBe(5); +     input('searchText').enter('a'); +     expect(tr.count()).toBe(2); + +  }); + </doc:scenario> +</doc:example> + +## Filters + +When presenting data to the user, you might need to convert the data from its raw format to a +user-friendly format. For example, you might have a data object that needs to be formatted +according to the locale before displaying it to the user. You can pass expressions through a +chain of filters like this: + +<pre> +name | uppercase +</pre> + +The expression evaluator simply passes the value of name to angular.filter.uppercase. + +Chain filters using this syntax: + +<pre> +value | filter1 | filter2 +</pre> + +You can also pass colon-delimited arguments to filters, for example, to display the number 123 +with 2 decimal points: 123 | number:2 + +# The $ + +You might be wondering, what is the significance of the $ prefix? It is simply a prefix that +angular chooses to differentiate its API names from others. If angular didn't use $, then +evaluating a.length() would return undefined because neither a nor angular define such a property. +Consider that in a future version of angular we might choose to add a length method, in which case +the behavior of the expression would change. Worse yet, you the developer could create a length +property and then we would have collision. This problem exists because angular augments existing +objects with additional behavior. By prefixing its additions with $ we are reserving our namespace +so that angular developers and developers who use angular can develop in harmony without +collisions. + + 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 <my:greeter/>, which should display a greeting. + +If we want this HTML source: + +<pre> +<div ng:init="salutation='Hello'; name='World'"> +  <my:greeter salutation="salutation" name="name"/> +</div> +</pre> + +To produce this DOM: + +<pre> +<div ng:init="salutation='Hello'; name='World'"> +  <my:greeter salutation="salutation" name="name"/> +    <span class="salutation">Hello</span> +    <span class="name">World</span>! +  </my:greeter> +</div> +</pre> + +Write this widget definition (assuming you've already declared the my namespace in the page): + + +<pre> +angular.widget('my:greeter', function(compileElement){ +  var compiler = this; +  compileElement.css('display', 'block'); +  var salutationExp = compileElement.attr('salutation'); +  var nameExp = compileElement.attr('name'); +  return function(linkElement){ +    var salutationSpan = angular.element('<span class="salutation"></span'); +    var nameSpan = angular.element('<span class="name"></span>'); +    linkElement.append(salutationSpan); +    linkElement.append(compiler.text(' ')); +    linkElement.append(nameSpan); +    linkElement.append(compiler.text('!')); +    this.$watch(salutationExp, function(value){ +      salutationSpan.text(value); +    }); +    this.$watch(nameExp, function(value){ +    nameSpan.text(value); +    }); +  }; +}); +</pre> + +Note: For more about widgets, see {@link 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 <span> element set to the salutation class +   * Create a <span> 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: + + > <div class="ng-exception">Error message</div> + + + +You can try to evaluate malformed expressions in {@link angualr.expression expression} to see the +`ng-exception` class' styling. + +## `ng-validation-error` + +**Usage:** angular applies this class to an input widget element if that element's input does not +pass validation. Note that you set the validation criteria on the input widget element using the +Ng:validate or Ng:required directives. + +**Styling:** The built-in styling of the ng-validation-error class turns the border of the input +box red and includes a hovering UI element that includes more details of the validation error. You +can see an example in {@link angular.widget.@ng:validate ng:validate example}. + +## How to override the styles for built-in classes + +To override the styles for these built-in classes, you can do any of the following: + +Download the source code, edit angular.css, and host the source on your own server. +Create a local css file, overriding any styles that you'd like, and link to it from your HTML file +as you normally would: + +<pre> +<link href="yourfile.css" rel="stylesheet" type="text/css"> +</pre> 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}. + +<pre> +// 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; +  } +}); +</pre> + +At run-time we can access the `uniqueId` service by looking it up with the service locator like +this: + +<pre> +// 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); +</pre> + +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: + +<pre> +angular.service('gadgetFactory', function(uniqueId){ +  return function(){ +    return {gadgetId: uniqueId()}; +  }; +}, {$inject: ['uniqueId']}); +</pre> + +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. + +<pre> +// crate a root scope +var rootScope = angular.scope(); +// accesss the service locator +var myService = rootScope.$service('myService'); +</pre> + + + +# 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: + +<pre> +function MyController($route){ +  // configure the route service +  $route.when(...); +} +MyController.$inject = ['$route']; +</pre> + +In this example, the `MyController` constructor function takes one argument, the +{@link 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: + +<pre> +<!doctype html> +<html xmlns:ng="http://angularjs.org" ng:controller="MyController"> + <script src="http://code.angularjs.org/angular.min.js" ng:autobind></script> + <body> +  ... + </body> +</html> +</pre> + +When angular is instantiating your controller, it needs to know what services, if any, should be +injected (passed in as arguments) into the controller. Since there is no reflection in JavaScript, +we have to supply this information to angular in the form of an additional property on the +controller constructor function called `$inject`. Think of it as annotations for JavaScript. + +<pre> +MyController.$inject = ['$route']; +</pre> + +The information in `$inject` is then used by the {@link 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. + +<pre> +// 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'); +</pre> + + +## Creating Controllers using Dependency Injection + +In a typical angular application the dependency injection is most commonly used when creating +controllers. +<pre> +// 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. +</pre> + + +## 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. + +<pre> +// 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'); +</pre> + + + +# 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: + +<pre> +function myFn(a,b){} +expect(myFn.toString()).toEqual('function myFn(a,b){}'); +</pre> + +This means that angular can infer the function names after all and use that information to generate +the `$inject` annotation automatically. Therefore the following two function definitions are +equivalent: + +<pre> +// given a user defined service +angular.service('serviceA', ...); + +// inject '$window', 'serviceA', curry 'name'; +function fnA($window, serviceA, name){}; +fnA.$inject = ['$window', 'serviceA']; + +// inject '$window', 'serviceA', curry 'name'; +function fnB($window, serviceA_, name){}; +// implies: fnB.$inject = ['$window', 'serviceA']; +</pre> + +If angular does not find 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 + + +* <a href="#H1_1">What Is Angular?</a> +* <a href="#H1_3">The Angular Philosophy</a> +* <a href="#H1_2">Anatomy Of An Angular App</a> +* <a href="#H1_4">Why You Want Angular</a> +* <a href="#H1_5">Angular's Ancestors</a> +* <a href="#H1_6">Watch a Presentation About Angular</a> + + +<a name="H1_1"></a> +# 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: + +<doc:example> +<doc:source> + <h2>Bigg Bike Shop</h2> + <hr> + <b>Invoice:</b> + <br/> + <br/> + <table> +  <tr><td> </td><td> </td> +  <tr><td>Quantity</td><td>Cost</td></tr> +  <tr> +    <td><input name="qty" value="1" ng:validate="integer:0" ng:required/></td> +    <td><input name="cost" value="19.95" ng:validate="number" ng:required/></td> +  </tr> + </table> + <hr> + <b>Total:</b> {{qty * cost | currency}} + <hr> +</doc:source> +<!-- +<doc:scenario> + it('should show of angular binding', function(){ +   expect(binding('qty * cost')).toEqual('$19.95'); +   input('qty').enter('2'); +   input('cost').enter('5.00'); +   expect(binding('qty * cost')).toEqual('$10.00'); + }); +</doc:scenario> +--> +</doc:example> + +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 <html xmlns:ng="http://angularjs.org"> + +This ensures angular runs nicely in all major browsers. + +In line __3__ we do two angular setup tasks inside a `<script>` tag: + +1. We pull in `angular.js`. +2. The angular {@link angular.directive.ng:autobind ng:autobind} directive tells angular to {@link +guide.compiler compile} and manage the whole HTML document. + +       3 <script src="file:///Users/krculp/angular.js/build/angular.min.js" ng:autobind></script> + +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: <input name="qty" value="1" ng:validate="integer:0" ng:required/> +       15 Cost: <input name="cost" value="199.95" ng:validate="number" ng:required/> + +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." + + +<a name="H1_3"></a> +# 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): + +<pre> +<span class="label">Hello</span> +</pre> + +Or, as In programmatic systems (like {@link http://code.google.com/webtoolkit/ GWT}), you would +have to write the code and then run the code like this: + +<pre> +var label = new Label(); +label.setText('Hello'); +label.setClass('label'); +parent.addChild(label); +</pre> + +That looks like, let's see, do some math, factor out the `<pre>`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. + + +<a name="H1_2"></a> +# 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: + +<img class="left" src="img/angular_parts.png" border="0" /> + + +<a name="H1_4"></a> +# 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. + + +<a name="H1_5"></a> +# 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. +`<HTML><BODY>Thank You, TB-L!</BODY></HTML>`. + +## 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. + + +<a name="H1_6"></a> +# Watch a Presentation About Angular + +Here is an early presentation on angular, but note that substantial development has occurred since +the talk was given in July of 2010. + +<object width="480" height="385"> + <param name="movie" value="http://www.youtube.com/v/elvcgVSynRg&hl=en_US&fs=1"></param> + <param name="allowFullScreen" value="true"></param> + <param name="allowscriptaccess" value="always"></param> + <embed src="http://www.youtube.com/v/elvcgVSynRg&hl=en_US&fs=1" +        type="application/x-shockwave-flash" allowscriptaccess="always" +        allowfullscreen="true" width="480" height="385"></embed> +</object> + +{@link +https://docs.google.com/present/edit?id=0Abz6S2TvsDWSZDQ0OWdjaF8yNTRnODczazdmZg&hl=en&authkey=CO-b7oID +Presentation} +| +{@link +https://docs.google.com/document/edit?id=1ZHVhqC0apbzPRQcgnb1Ye-bAUbNJ-IlFMyPBPCZ2cYU&hl=en&authkey=CInnwLYO +Source} 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 + +<a name="H1_1"></a> +# Open Source + +`Angular` is an open source project licensed under the {@link +http://github.com/angular/angular.js/blob/master/LICENSE MIT license}. Your contributions are +always welcome. When working with `angular` source base, please follow the guidelines provided on +this page. + +* <a href="#H1_2">Contributing to Source Code</a> +* <a href="#H1_3">Applying Code Standards</a> +* <a href="#H1_4">Checking Out and Building `Angular`</a> +* <a href="#H1_5">Submitting Your Changes</a> + + + +<a name="H1_2"></a> +# Contributing to Source Code + +We'd love for you to contribute to our source code and to make `angular` even better than it is +today! Here are the guidelines we'd like you to use: + +* Major changes that you intend to contribute to the project must be discussed first on our {@link +https://groups.google.com/forum/?hl=en#!forum/angular mailing list} so that we can better +coordinate our efforts, prevent  duplication of work, and help you to craft the change so that it +is successfully accepted upstream. +* Small changes and bug fixes can be crafted and submitted to Github as a <a href="#H1_5">pull +request</a>. + + + +<a name="H1_3"></a> +# Applying Code Standards + +To ensure consistency throughout the source code, keep these rules in mind as you are working: + +* All features or bug fixes must be tested by one or more <a href="#unit-tests">specs</a>. +* All public API methods must be documented with ngdoc, an extended version of jsdoc (we added +support for markdown and templating via `@ngdoc` tag). To see how we document our APIs, please +check out the existing ngdocs. +* With the exceptions listed below, we follow the rules contained in {@link +http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml Google's JavaScript Style +Guide}: + +  * Do not use namespaces: Instead, we wrap the entire `angular` code base in an anonymous closure +and export our API explicitly rather than implicitly. +  * Wrap all code at 100 characters. +  * Instead of complex inheritance hierarchies, we prefer simple objects. We use prototypical +inheritance only when absolutely necessary. +  * We love functions and closures and, whenever possible, prefer them over objects. +  * To write concise code that can be better minified, internally we use aliases that map to the +external API. See our existing code to see what we mean. +  * We don't go crazy with type annotations for private internal APIs unless it's an internal API +that is used throughout `angular`. The best guidance is to do what makes the most sense. + + +<a name="H1_4"></a> +# Checking Out and Building Angular + +The `angular` source code is hosted at {@link http://github.com Github}, which we also use to +accept code contributions. Several steps are needed to check out and build `angular`: + + +## Installation Dependencies + +Before you can build `angular`, you must install or configure the following dependencies on your +machine: + +* {@link http://rake.rubyforge.org Rake}: We use Rake as our build system, which is pre-installed +on most Macintosh and Linux machines. If that is not true in your case, you can grab it from the +Rake website. + +* {@link http://nodejs.org Node.js}: We use Node to generate the documentation and to run a +development web server. Depending on your system, you can install Node either from source or as a +pre-packaged bundle. + +* Java: The Java runtime is used to run {@link http://code.google.com/p/js-test-driver +JsTestDriver} (JSTD), which we use to run our unit test suite. JSTD binaries are part of the +`angular` source base, which means there is no need to install or configure it separately. +* Git: The {@link http://help.github.com/mac-git-installation Github Guide to Installing Git} is +quite a good source for information on Git. + + +## Creating a Github Account and Forking Angular + +To create a Github account, follow the instructions {@link https://github.com/signup/free here}. +Afterwards, go ahead and {@link http://help.github.com/forking fork} the {@link +https://github.com/angular/angular.js main angular repository}. + + +## Building `Angular` + +To build `angular`, you check out the source code and use Rake to generate the non-minified and +minified `angular` files: + +1. To clone your Github repository, run: + +        git clone git@github.com:<github username>/angular.js.git + +2. To go to the `angular` directory, run: + +        cd angular.js + +3. To add the main `angular` repository as an upstream remote to your repository, run: + +        git remote add upstream https://github.com/angular/angular.js.git + +4. To build `angular`, run: + +        rake package + +The build output can be located under the `build` directory. It consists of the following files and +directories: + +* `angular-x.y.z-<git sha>.tgz` — This is the complete tarball, which  contains all of the release +build artifacts. +* `angular.js` — The non-minified `angular` script. +* `angular.min.js` —  The minified `angular` script. +* `angular-scenario.js` — The `angular` End2End test runner. +* `angular-ie-compat.js` —  The Internet Explorer compatibility patch file. +* `docs/` — A directory that contains all of the files needed to run `docs.angularjs.org`. +* `docs/index.html` — The main page for the documentation. +* `docs/docs-scenario.html` — The End2End test runner for the documentation application. + + +## Running a Local Development Web Server + +To debug or test code, it is often useful to have a local HTTP server. For this purpose, we have +made available a local web server based on Node.js. + +1. To start the web server, run: + +        ./nodeserver.sh + +2. To access the local server, go to this website: + +        http://localhost:8000/ + +   By default, it serves the contents of the `angular` project directory. + + +<a name="unit-tests"></a> +## Running the Unit Test Suite + +Our unit and integration tests are written with Jasmine and executed with JsTestDriver.  To run the +tests: + +1. To start the JSTD server, run: + +        ./server.sh + +2. To capture one or more browsers, go to this website: + +        http://localhost:9876/ + +3. To trigger a test execution, run: + +         ./test.sh + +4. To automatically run the test suite each time one or more of the files in the project directory +is changed, you can install `watchr` and then run: + +        watchr watchr.rb + +5. To view the output of each test run, you can tail this log file: + +        ./logs/jstd.log + + +## Running the End2End Test Suite + +To run the End2End test suite: + +1. Start the local web server. +2. In a browser, go to: + +        http://localhost:8000/build/docs/docs-scenario.html + +   The tests are executed automatically. + + + +<a name="H1_5"></a> +# Submitting Your Changes + +To create and submit a change: + +1. Create a new branch off the master for your changes: + +        git branch my-fix-branch + +2. Check out the branch: + +        git checkout my-fix-branch + +3. Create your patch, make sure to have plenty of tests (that pass). + +4. Commit your changes: + +        git commit -a + +5. Run JavaScript Lint and be sure to address all new warnings and errors: + +        rake lint + +6. Push your branch to Github: + +        git push origin my-fix-branch + +7. In Github, send a pull request to `angular:master`. + +8. When the patch is reviewed and merged, delete your branch and pull yours — and other — changes +from the main (upstream) repository: +  * 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 <http://code.angularjs.org/> urls. + +There are two kinds of urls you care about: + +* http://code.angularjs.org/angular-<version>.js +* http://code.angularjs.org/angular-<version>.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: + +<pre> +  <!doctype html> +  <html> +    <head> +      <title>My Angular App</title> +      <script src="http://code.angularjs.org/angular-0.9.12.js" ng:autobind></script> +    </head> +    <body> +    </body> +  </html> +</pre> + + +# Downloading and hosting the files on your own + +This options is for those who want to work with angular offline, or want to host the angular files +on their own servers. + +If you navigate to <http://code.angularjs.org/>, you'll see a directory listing with all angular +versions since we started releasing versioned build artifacts (quite late in the project lifetime). + +Each directory contains all artifacts that we released for a particular version. Once you navigate +to one of these directories you'll see the following list of files: + +* `angular-<version>.js` - This file is non-obfuscated, non-minified, and human-readable by opening +it it any editor or browser. In order to get better error messages during development, you should +always use this non-minified angular script. + +* `angular-<version>.min.js` - This is a minified and obfuscated version of +`angular-<version>.js` created with Closure compiler. Use this version for production in order to +minimize the size of the application that is downloaded by your user's browser. + +* `angular-<version>.tgz` - This is a tarball archive which contains all the other files released +for this angular version. Use this file to get everything in a single download. + +* `angular-ie-compat-<version>.js` - This is a special file that contains code and data specifically +tailored for getting Internet Explorer to work with angular. If you host your own copy of angular +files, make sure that this file is available for download and resides under the same parent path as +`angular-<version>.js` or `angular-<version>.min.js`. + +* `angular-mocks-<version>.js` - This file contains implementation of mocks that we provide to you +to make testing angular apps even easier. Your unit/integration test harness should load this file +after `angular-<version>.js` is loaded. + +* `angular-scenario-<version>.js` - This file is a very nifty JavaScript file, which allows you to +write and execute end to end tests for angular applications. + +* `docs-<version>` - this directory contains all the files that compose the +<http://docs.angularjs.org/> documentation app. These files are handy to see the older version of +our docs, or even more importantly, view the docs offline! 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 <angular/> an HTML5 tag? + +No, <angular/> is not an HTML5 tag. angular is an orthogonal project to HTML5; you can use the two +together. + +### Is angular a {library, framework, DOM manipulation library, widget library, native plugin}? + +No, angular is none of these. You don't call its functions, it does not call your functions, +it does not provide a way to manipulate DOM, but does provide primitives to create UI projections +of your data. There are lots of existing widget libraries which you can integrate with angular. +It is 100% JavaScript, 100% client side and compatible with both desktop and mobile browsers. + +### Do I need to worry about security holes in angular? + +Like with any technology, angular is not impervious to attack. angular does, however, provide +built-in protection from basic security holes including cross-site scripting and HTML injection +attacks. angular does round-trip escaping on all strings for you. + +### Can I download the source, build, and host the angular environment locally? + +Yes. See instructions in {@link downloading 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. + +<doc:example> +    <doc:source> +        Hello {{'World'}}! +    </doc:source> +</doc:example> + +The resulting web page should look something like the following: + +<img class="center" src="img/helloworld.png" border="1" /> + +Now let's take a closer look at that code, and see what is going on behind  +the scenes.   + +The first line of interest defines the `ng` namespace, which makes  +`angular` work across all browsers (especially important for IE): + +<pre> +    <html xmlns:ng="http://angularjs.org"> +</pre> + +The next line downloads the `angular` script, and instructs `angular` to process  +the entire HTML page when it is loaded: + +<pre> +    <script type="text/javascript" src="http://code.angularjs.org/angular-?.?.?.min.js" ng:autobind></script> +</pre> + +(For details on what happens when `angular` processes an HTML page,  +see {@link guide.bootstrap Bootstrap}.) + +Finally, this line in the `<body>` of the page is the template that describes  +how to display our greeting in the UI: + +<pre> +    Hello {{'World'}}! +</pre> + +Note the use of the double curly brace markup (`{{ }}`) to bind the expression to  +the greeting text. Here the expression is the string literal 'World'. + +Next let's look at a more interesting example, that uses `angular` to  +bind a dynamic expression to our greeting text. + +# Hello <angular/> World! + +This example demonstrates `angular`'s two-way data binding:  + +1. Edit the HTML file you created in the "Hello World!" example above. +2. Replace the contents of `<body>` with the code from the __Source__ box below. +3. Refresh your browswer window. + +<doc:example> + <doc:source> +  Your name: <input type="text" name="yourname" value="World"/> +  <hr/> +  Hello {{yourname}}! + </doc:source> +</doc:example> + +After the refresh, the page should look something like this: + +<img class="left" src="img/helloworld_2way.png" border="1" /> + +These are some of the important points to note from this example: + +* The text input {@link angular.widget widget} called `yourname` is bound to a model variable called  +  `yourname`. +* The double curly braces notation binds the variable `yourname` to the  +  greeting text. +<!-- +* The variable `yourname` is implicitly created in the root scope. +--> +* You did not need to explicitly register an event listener or define an event  +  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: + +<img class="left" src="img/angular_parts.png" border="0" /> + +In addition, `angular` comes with a set of Services, which have the following  +properties: + +* The services provided are very useful for building web applications. +* You can extend and add application-specific behavior to services. +* Services include Dependency-Injection, XHR, caching, URL routing,  +  and browser abstraction. + +# Where To Go Next + +* For 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: + +* <a href="#UsingGit">Using Git</a>. Use the Git versioning system to get the files for each step. +* <a href="#UsingSnapshots">Using Snapshots</a>. Download snapshots (files for each step of the +tutorial) and tinker with them. +* <a href="#ReadingExamples">Reading the Examples</a>. 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. + +<a name="PreReqs"></a> +# 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. + +<a name="UsingGit"></a> +# 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 <a href="#PreReqs">prerequisites</a> 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". + + + +<a name="UsingSnapshots"></a> +# 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 <a href="#PreReqs">prerequisites</a> 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. + +<a name="ReadingExamples"></a> +# 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
 +
 +<table id="tutorial_nav">
 +<tr>
 +<td id="previous_step">{@link tutorial Previous}</td>
 +<td id="step_result">{@link  http://angular.github.com/angular-phonecat/step-0/app Example}</td>
 +<td id="tut_home">{@link tutorial Tutorial Home}</td>
 +<td id="code_diff">Code Diff</td>
 +<td id="next_step">{@link tutorial.step_01 Next}</td>
 +</tr>
 +</table>
 +
 +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`:__
 +<pre>
 +<!doctype html>
 +<html xmlns:ng="http://angularjs.org/">
 +<head>
 +  <meta charset="utf-8">
 +  <title>my angular app</title>
 +  <link rel="stylesheet" href="css/app.css"/>
 +</head>
 +<body>
 +
 +  Nothing here yet!
 +
 +  <script src="lib/angular/angular.js" ng:autobind></script>
 +</body>
 +</html>
 +</pre>
 +
 +## 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).
 +
 +* __`<script src="lib/angular/angular.js"` ...__ This downloads the `angular.js` script and
 +registers a callback that will be executed by the browser when the containing HTML page is fully
 +downloaded. When the callback is executed, angular looks for the {@link
 +angular.directive.ng:autobind ng:autobind} attribute. If `ng:autobind` is found, it signals
 +angular to bootstrap and compile and manage the whole html page.
 +
 +    Note: If you elected not to download any tutorial files but still want to try out some angular
 +    code on your system, you can change the relative path to the `angular.js` script in your
 +    template from `./lib/angular/angular.js` to the following:
 +
 +            <script src="http://code.angularjs.org/angular-0.9.14.js" ng:autobind></script>
 +
 +    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.
 +
 +<table id="tutorial_nav">
 +<tr>
 +<td id="previous_step">{@link tutorial Previous}</td>
 +<td id="step_result">{@link  http://angular.github.com/angular-phonecat/step-0/app Example}</td>
 +<td id="tut_home">{@link tutorial Tutorial Home}</td>
 +<td id="code_diff">Code Diff</td>
 +<td id="next_step">{@link tutorial.step_01 Next}</td>
 +</tr>
 +</table>
 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
 +<table id="tutorial_nav">
 +  <tr>
 +   <td id="previous_step">{@link tutorial.step_00 Previous}</td>
 +    <td id="step_result">{@link http://angular.github.com/angular-phonecat/step-1/app Example}</td>
 +    <td id="tut_home">{@link tutorial Tutorial Home}</td>
 +    <td id="code_diff">
 +{@link https://github.com/angular/angular-phonecat/compare/step-0...step-1 Code Diff}</td>
 +    <td id="next_step">{@link tutorial.step_02 Next}</td>
 +  </tr>
 +</table>
 +
 +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:
 +
 +        ...
 +        <html xmlns:ng="http://angularjs.org">
 +        ...
 +
 +Let's add the following code to `index.html`:
 +
 +__`app/index.html`:__
 +<pre>
 +<head>
 +...
 +  <title>Google Phone Gallery</title>
 +...
 +</head>
 +...
 +  <ul>
 +    <li>
 +      <span>Nexus S<span>
 +      <p>
 +        Fast just got faster with Nexus S.
 +      </p>
 +    </li>
 +    <li>
 +      <span>Motorola XOOM™ with Wi-Fi<span>
 +      <p>
 +        The Next, Next Generation tablet.
 +      </p>
 +    </li>
 +  </ul>
 +...
 +</pre>
 +
 +## 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.
 +
 +<table id="tutorial_nav">
 +  <tr>
 +   <td id="previous_step">{@link tutorial.step_00 Previous}</td>
 +    <td id="step_result">{@link http://angular.github.com/angular-phonecat/step-1/app Example}</td>
 +    <td id="tut_home">{@link tutorial Tutorial Home}</td>
 +    <td id="code_diff">
 +{@link https://github.com/angular/angular-phonecat/compare/step-0...step-1 Code Diff}</td>
 +    <td id="next_step">{@link tutorial.step_02 Next}</td>
 +  </tr>
 +</table>
 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
 +<table id="tutorial_nav">
 + <tr>
 +   <td id="previous_step">{@link tutorial.step_01 Previous}</td>
 +   <td id="step_result">{@link  http://angular.github.com/angular-phonecat/step-2/app Example}</td>
 +   <td id="tut_home">{@link tutorial Tutorial Home}</td>
 +<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-1...step-2 Code
 +Diff}</td>
 +   <td id="next_step">{@link tutorial.step_03 Next}</td>
 + </tr>
 +</table>
 +
 +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`:__
 +<pre>
 +...
 +<body ng:controller="PhoneListCtrl">
 +
 +  <ul>
 +    <li ng:repeat="phone in phones">
 +      {{phone.name}}
 +      <p>{{phone.snippet}}</p>
 +    </li>
 +  </ul>
 +
 +  <script src="lib/angular/angular.js" ng:autobind></script>
 +  <script src="js/controllers.js"></script>
 +</body>
 +...
 +</pre>
 +
 +Our data __Model__ (a short list of phones in object literal notation) is instantiated within our
 +__Controller__ function (`PhoneListCtrl`):
 +
 +__`app/js/controllers.js`:__
 +<pre>
 +/* 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."}];
 +}
 +</pre>
 +
 +The "Angular way" urges us to test as we develop:
 +
 +__`test/unit/controllersSpec.js`:__
 +<pre>
 +/* 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);
 +    });
 +  });
 +});
 +</pre>
 +
 +## 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 `<li>` tag is an angular repeater.  It
 +    tells angular to create a `<li>` element for each phone in the phones list, using the first
 +    `<li>` tag as the template.
 +
 +    * 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 `<body>` tag
 +    (`PhoneListCtrl`).
 +    * We instantiated our data within the scope of our controller function, and our template
 +    binding points are located within the block bounded by the `<body
 +    ng:controller="PhoneListCtrl>` tag.
 +
 +    Angular uses scopes, along with the information contained in the template, data model, and
 +    controller to keep the Model and View separated but in sync: any changes to the model are
 +    reflected in the view; any changes that occur in the view are reflected in the model.
 +
 +* __Model:__ For our data model, we created a simple array of phone records, specified in object
 +literal notation.
 +
 +* __Testing:__ Ease of testing is another cornerstone of angular's design philosophy.  All we are
 +doing here is showing how easy it is to create a unit test using the technology baked into
 +angular.  The test verifies that we have 3 records in the phones array.
 +
 +    To run this test, make sure you have a {@link tutorial test server running}, and type
 +    `./scripts/test.sh` from the command line.
 +
 +    Angular developers prefer the syntax of Jasmine's Behavior-driven Development (BDD) framework
 +    when writing tests. So while Jasmine is not required by angular, we use it to write all tests
 +    in this tutorial. You can learn about Jasmine on the {@link http://pivotal.github.com/jasmine/
 +    Jasmine home page} and on the {@link https://github.com/pivotal/jasmine/wiki Jasmine wiki}.
 +
 +<table id="tutorial_nav">
 + <tr>
 +   <td id="previous_step">{@link tutorial.step_01 Previous}</td>
 +   <td id="step_result">{@link  http://angular.github.com/angular-phonecat/step-2/app Example}</td>
 +   <td id="tut_home">{@link tutorial Tutorial Home}</td>
 +<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-1...step-2 Code
 +Diff}</td>
 +   <td id="next_step">{@link tutorial.step_03 Next}</td>
 + </tr>
 +</table>
 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
 +<table id="tutorial_nav">
 +<tr>
 +  <td id="previous_step">{@link tutorial.step_02 Previous}</td>
 +  <td id="step_result">{@link  http://angular.github.com/angular-phonecat/step-3/app Example}</td>
 +  <td id="tut_home">{@link tutorial Tutorial Home}</td>
 +  <td id="code_diff">{@link
 +  https://github.com/angular/angular-phonecat/commit/a03815f8fb00217f5f9c1d3ef83282f79818e706 Code
 +  Diff}</td>
 +  <td id="next_step">{@link tutorial.step_04 Next}</td>
 +</tr>
 +</table>
 +
 +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`:__
 +<pre>
 +...
 +   Fulltext Search: <input name="query"/>
 +
 +  <ul class="phones">
 +    <li ng:repeat="phone in phones.$filter(query)">
 +      {{phone.name}}
 +      <p>{{phone.snippet}}</p>
 +    </li>
 +  </ul>
 +...
 +</pre>
 +__`test/e2e/scenarios.js`:__
 +<pre>
 +/* 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);
 +    });
 +  });
 +});
 +</pre>
 +
 +## 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 `<input>` 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.
 +
 +<table id="tutorial_nav">
 +<tr>
 +  <td id="previous_step">{@link tutorial.step_02 Previous}</td>
 +  <td id="step_result">{@link  http://angular.github.com/angular-phonecat/step-3/app Example}</td>
 +  <td id="tut_home">{@link tutorial Tutorial Home}</td>
 +  <td id="code_diff">{@link
 +  https://github.com/angular/angular-phonecat/commit/a03815f8fb00217f5f9c1d3ef83282f79818e706 Code
 +  Diff}</td>
 +  <td id="next_step">{@link tutorial.step_04 Next}</td>
 +</tr>
 +</table>
 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
 +<table id="tutorial_nav">
 +<tr>
 +<td id="previous_step">{@link tutorial.step_03 Previous}</td>
 +<td id="step_result">{@link  http://angular.github.com/angular-phonecat/step-4/app Example}</td>
 +<td id="tut_home">{@link tutorial Tutorial Home}</td>
 +<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-3...step-4 Code
 +Diff}</td>
 +<td id="next_step">{@link tutorial.step_05 Next}</td>
 +</tr>
 +</table>
 +
 +In this step, we add a feature that lets our users choose which way to order the phone list.
 +
 +__`app/index.html`:__
 +<pre>
 +...
 +  <ul class="predicates">
 +    <li>
 +      Search: <input type="text" name="query"/>
 +    </li>
 +    <li>
 +      Sort by:
 +      <select name="orderProp">
 +        <option value="name">Alphabetical</option>
 +        <option value="age">Newest</option>
 +      </select>
 +    </li>
 +  </ul>
 +
 +  <ul class="phones">
 +    <li ng:repeat="phone in phones.$filter(query).$orderBy(orderProp)">
 +      {{phone.name}}
 +      <p>{{phone.snippet}}</p>
 +    </li>
 +  </ul>
 +...
 +</pre>
 +
 +__`app/js/controller.js`:__
 +<pre>
 +/* 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';
 +}
 +</pre>
 +
 +__`test/unit/controllerSpec.js`:__
 +<pre>
 +/* 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');
 +    });
 +  });
 +});
 +</pre>
 +
 +__`test/e2e/scenarios.js`:__
 +<pre>
 +/* 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"]);
 +    });
 +  });
 +});
 +</pre>
 +
 +## 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 `<select>` element named `orderProp` for our users so they can choose to
 +sort the phone list either alphabetically or by the age of the phone.  We added the `age` property
 +to each phone record so we can sort by that field.
 +
 +* Like {@link angular.Array.filter $filter}, {@link angular.Array.orderBy $orderBy} is a built-in
 +method available on array objects in angular expressions.  In our UI template, we set up a select
 +box that lets the user set the `orderProp` model variable to one of the string constants: `age` or
 +`name`.
 +
 +* In our controller, we added a line to set the default value of `orderProp` to `age`.  If we
 +don't override the default value, angular uses the value of the first `<option>` element when it
 +initializes the data model.
 +
 +* Our unit test now verifies that our default ordering property is set.
 +
 +* We added an end-to-end test to verify that our select box ordering mechanism works properly.
 +
 +* Once again we added a little more CSS to improve the View.
 +
 +<table id="tutorial_nav">
 +<tr>
 +<td id="previous_step">{@link tutorial.step_03 Previous}</td>
 +<td id="step_result">{@link  http://angular.github.com/angular-phonecat/step-4/app Example}</td>
 +<td id="tut_home">{@link tutorial Tutorial Home}</td>
 +<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-3...step-4 Code
 +Diff}</td>
 +<td id="next_step">{@link tutorial.step_05 Next}</td>
 +</tr>
 +</table>
 diff --git a/docs/content/tutorial/step_05.ngdoc b/docs/content/tutorial/step_05.ngdoc new file mode 100755 index 00000000..8ec0fca4 --- /dev/null +++ b/docs/content/tutorial/step_05.ngdoc @@ -0,0 +1,147 @@ +@workInProgress
 +@ngdoc overview
 +@name Tutorial: Step 5
 +@description
 +<table id="tutorial_nav">
 +<tr>
 + <td id="previous_step">{@link tutorial.step_04 Previous}</td>
 + <td id="step_result">{@link  http://angular.github.com/angular-phonecat/step-5/app Example}</td>
 + <td id="tut_home">{@link tutorial Tutorial Home}</td>
 +<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-4...step-5 Code
 +Diff}</td>
 + <td id="next_step">{@link tutorial.step_06 Next}</td>
 +</tr>
 +</table>
 +
 +In this step, the View template remains the same but the Model and Controller change.  We'll
 +introduce the use of an angular {@link angular.service service}, which we will use to implement an
 +`XMLHttpRequest` request to communicate with a server. Angular provides the built-in {@link
 +angular.service.$xhr $xhr} service to make this easy.
 +
 +The addition of the `$xhr` service to our app gives us the opportunity to talk about {@link
 +guide.di Dependency Injection} (DI).  The use of DI is another cornerstone of the angular
 +philosophy. DI helps make your web apps well structured, loosely coupled, and ultimately easier to
 +test.
 +
 +__`app/js/controllers.js:`__
 +<pre>
 +/* 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'];
 +</pre>
 +
 +__`test/unit/controllerSpec.js`:__
 +<pre>
 +/* jasmine specs for controllers go here */
 +describe('PhoneCat controllers', function() {
 +
 +  describe('PhoneListCtrl', function(){
 +    var scope, $browser, ctrl;
 +
 +    beforeEach(function() {
 +      scope = angular.scope();
 +      $browser = scope.$service('$browser');
 +
 +      $browser.xhr.expectGET('phones/phones.json').respond([{name: 'Nexus S'},
 +                                                            {name: 'Motorola DROID'}]);
 +      ctrl = scope.$new(PhoneListCtrl);
 +    });
 +
 +
 +    it('should create "phones" model with 2 phones fetched from xhr', function() {
 +      expect(ctrl.phones).toBeUndefined();
 +      $browser.xhr.flush();
 +
 +      expect(ctrl.phones).toEqual([{name: 'Nexus S'},
 +                                   {name: 'Motorola DROID'}]);
 +    });
 +
 +
 +    it('should set the default value of orderProp model', function() {
 +      expect(ctrl.orderProp).toBe('age');
 +    });
 +  });
 +});
 +</pre>
 +
 +## Discussion:
 +
 +* __Services:__ {@link angular.service Services} are substitutable objects managed by angular's
 +{@link guide.di DI subsystem}.  Angular services simplify some of the standard operations common
 +to web apps.  Angular provides several built-in services (such as {@link angular.service.$xhr
 +$xhr}). You can also create your own custom services.
 +
 +* __Dependency Injection:__ To use an angular service, you simply provide the name of the service
 +as an argument to the controller's constructor function. The name of the argument is significant,
 +because angular's {@link guide.di DI subsystem} recognizes the identity of a service by its name,
 +and provides the name of the service to the controller during the controller's construction. The
 +dependency injector also takes care of creating any transitive dependencies the service may have
 +(services often depend upon other services).
 +
 +  Note: if you minify the javascript code for this controller, all function arguments will be
 +  minified as well. This will result in the dependency injector not being able to identify
 +  services correctly. To overcome this issue, just assign an array with service identifier strings
 +  into the `$inject` property of the controller function.
 +
 +* __`$xhr`:__ We moved our data set out of the controller and into the file
 +`app/phones/phones.json` (and added some more phones). We used the `$xhr` service to make a GET
 +HTTP request to our web server, asking for `phone/phones.json` (the url is relative to our
 +`index.html` file). The server responds with the contents of the json file, which serves as the
 +source of our data. Keep in mind that the response might just as well have been dynamically
 +generated by a sophisticated backend server. To our web server they both look the same, but using
 +a real backend server to generate a response would make our tutorial unnecessarily complicated.
 +
 +    Notice that the $xhr service takes a callback as the last parameter. This callback is used to
 +    process the response. In our case, we just assign the response to the current scope controlled
 +    by the controller, as a model called `phones`. Have you realized that we didn't even have to
 +    parse the response? Angular took care of that for us.
 +
 +* __Testing:__  The unit tests have been expanded. Because of the dependency injection business,
 +we now need to create the controller the same way that angular does it behind the scenes. For this
 +reason, we need to:
 +
 +    * Create a root scope object by calling `angular.scope()`
 +
 +    * Call `scope.$new(PhoneListCtrl)` to get angular to create the child scope associated with
 +    our controller.
 +
 +    At the same time, we need to tell the testing harness that it should expect an incoming
 +    request from our controller. To do this we:
 +
 +    * Use the `$service` method to retrieve the `$browser` service - this is a service that in
 +    angular represents various browser APIs. In tests, angular automatically uses a mock version
 +    of this service that allows you to write tests without having to deal with these native APIs
 +    and the global state associated with them.
 +
 +    * We use the `$browser.expectGET` method to train the `$browser` object to expect an incoming
 +    http request and tell it what to respond with. Note that the responses are not returned before
 +    we call the `$browser.xhr.flush()` method.
 +
 +    * We then make assertions to verify that the `phones` model doesn't exist on the scope, before
 +    the response is received.
 +
 +    * We flush the xhr queue in the browser by calling `$browser.xhr.flush()`. This causes the
 +    callback we passed into the `$xhr` service to be executed with the trained response.
 +
 +    * Finally, we make the assertions, verifying that the phone model now exists on the scope.
 +
 +<table id="tutorial_nav">
 +<tr>
 + <td id="previous_step">{@link tutorial.step_04 Previous}</td>
 + <td id="step_result">{@link  http://angular.github.com/angular-phonecat/step-5/app Example}</td>
 + <td id="tut_home">{@link tutorial Tutorial Home}</td>
 + <td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-4...step-5
 + Code Diff}</td>
 + <td id="next_step">{@link tutorial.step_06 Next}</td>
 +</tr>
 +</table>
 diff --git a/docs/content/tutorial/step_06.ngdoc b/docs/content/tutorial/step_06.ngdoc new file mode 100755 index 00000000..afe809a6 --- /dev/null +++ b/docs/content/tutorial/step_06.ngdoc @@ -0,0 +1,113 @@ +@workInProgress
 +@ngdoc overview
 +@name Tutorial: Step 6
 +@description
 +<table id="tutorial_nav">
 +<tr>
 +<td id="previous_step">{@link tutorial.step_05 Previous}</td>
 +<td id="step_result">{@link  http://angular.github.com/angular-phonecat/step-6/app Example}</td>
 +<td id="tut_home">{@link tutorial Tutorial Home}</td>
 +<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-5...step-6 Code
 +Diff}</td>
 +<td id="next_step">{@link tutorial.step_07 Next}</td>
 +</tr>
 +</table>
 +
 +In this step, we add thumbnail images, links, and a little more CSS to our app.  For now, our
 +links go nowhere.  One step at a time; in the next step we'll implement new views that these links
 +will open.
 +
 +__`app/index.html`:__
 +<pre>
 +...
 +  <ul class="predicates">
 +    <li>
 +      Search: <input type="text" name="query"/>
 +    </li>
 +    <li>
 +      Sort by:
 +      <select name="orderProp">
 +        <option value="name">Alphabetical</option>
 +        <option value="age">Newest</option>
 +      </select>
 +    </li>
 +  </ul>
 +
 +  <ul class="phones">
 +    <li ng:repeat="phone in phones.$filter(query).$orderBy(orderProp)">
 +      <a href="#/phones/{{phone.id}}">{{phone.name}}</a>
 +      <a href="#/phones/{{phone.id}}" class="thumb"><img ng:src="{{phone.imageUrl}}"></a>
 +      <p>{{phone.snippet}}</p>
 +    </li>
 +  </ul>
 +...
 +</pre>
 +
 +__`app/js/controller.js`__ (Unchanged):
 +<pre>
 +/* 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'];
 +</pre>
 +
 +__`app/phones/phones.json`__ (sample snippet):
 +<pre>
 + [
 +  {
 +   "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?"
 +  },
 +  …
 + ]
 +</pre>
 +
 +__`test/e2e/scenarios.js`__:
 +<pre>
 +...
 +    it('should render phone specific links', function() {
 +      input('query').enter('nexus');
 +      element('.phones li a').click();
 +      expect(browser().location().hash()).toBe('/phones/nexus-s');
 +    });
 +...
 +</pre>
 +
 +## 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 `<img>` 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.
 +
 +<table id="tutorial_nav">
 +<tr>
 +<td id="previous_step">{@link tutorial.step_05 Previous}</td>
 +<td id="step_result">{@link  http://angular.github.com/angular-phonecat/step-6/app Example}</td>
 +<td id="tut_home">{@link tutorial Tutorial Home}</td>
 +<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-5...step-6 Code
 +Diff}</td>
 +<td id="next_step">{@link tutorial.step_07 Next}</td>
 +</tr>
 +</table>
 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
 +<table id="tutorial_nav">
 +<tr>
 +<td id="previous_step">{@link tutorial.step_06 Previous}</td>
 +<td id="step_result">{@link  http://angular.github.com/angular-phonecat/step-7/app Live Demo
 +}</td>
 +<td id="tut_home">{@link tutorial Tutorial Home}</td>
 +<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-6...step-7 Code
 +Diff}</td>
 +<td id="next_step">{@link tutorial.step_08 Next}</td>
 +</tr>
 +</table>
 +
 +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`:__
 +<pre>
 +...
 +<body ng:controller="PhoneCatCtrl">
 +
 +  <ng:view></ng:view>
 +
 +  <script src="lib/angular/angular.js" ng:autobind></script>
 +  <script src="js/controllers.js"></script>
 +</body>
 +</html>
 +</pre>
 +
 +__`app/partials/phone-list.html`:__
 +<pre>
 +<ul class="predicates">
 +  <li>
 +    Search: <input type="text" name="query"/>
 +  </li>
 +  <li>
 +    Sort by:
 +    <select name="orderProp">
 +      <option value="name">Alphabetical</option>
 +      <option value="age">Newest</option>
 +    </select>
 +  </li>
 +</ul>
 +
 +<ul class="phones">
 +  <li ng:repeat="phone in phones.$filter(query).$orderBy(orderProp)">
 +    <a href="#/phones/{{phone.id}}">{{phone.name}}</a>
 +    <a href="#/phones/{{phone.id}}" class="thumb"><img ng:src="{{phone.imageUrl}}"></a>
 +    <p>{{phone.snippet}}</p>
 +  </li>
 +</ul>
 +</pre>
 +
 +__`app/partials/phone-list.html`:__
 +<pre>
 +TBD: detail view for {{params.phoneId}}
 +</pre>
 +
 +__`app/js/controller.js`:__
 +<pre>
 +/* 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() {}
 +</pre>
 +
 +## Discussion:
 +
 +* __The View.__ Our View template in `index.html` has been reduced down to this:
 +`<ng:view></ng:view>`.  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.
 +
 +<table id="tutorial_nav">
 +<tr>
 +<td id="previous_step">{@link tutorial.step_06 Previous}</td>
 +<td id="step_result">{@link  http://angular.github.com/angular-phonecat/step-7/app Live Demo
 +}</td>
 +<td id="tut_home">{@link tutorial Tutorial Home}</td>
 +<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-6...step-7 Code
 +Diff}</td>
 +<td id="next_step">{@link tutorial.step_08 Next}</td>
 +</tr>
 +</table>
 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
 +<table id="tutorial_nav">
 +<tr>
 +<td id="previous_step">{@link tutorial.step_07 Previous}</td>
 +<td id="step_result">{@link  http://angular.github.com/angular-phonecat/step-8/app Live Demo
 +}</td>
 +<td id="tut_home">{@link tutorial Tutorial Home}</td>
 +<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-7...step-8 Code
 +Diff}</td>
 +<td id="next_step">{@link tutorial.step_09 Next}</td>
 +</tr>
 +</table>
 +
 +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`:__
 +<pre>
 +<img ng:src="{{phone.images[0]}}" class="phone"/>
 +
 +<h1>{{phone.name}}</h1>
 +
 +<p>{{phone.description}}</p>
 +
 +<ul class="phone-thumbs">
 +  <li ng:repeat="img in phone.images">
 +    <img ng:src="{{img}}"/>
 +  </li>
 +</ul>
 +
 +<ul class="specs">
 +  <li>
 +    <span>Availability and Networks</span>
 +    <dl>
 +      <dt>Availability</dt>
 +      <dd ng:repeat="availability in phone.availability">{{availability}}</dd>
 +    </dl>
 +  </li>
 +    ...
 +  </li>
 +    <span>Additional Features</span>
 +    <dd>{{phone.additionalFeatures}}</dd>
 +  </li>
 +</ul>
 +</pre>
 +
 +__`app/js/controller.js`:__
 +<pre>
 +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'];
 +</pre>
 +
 +__`app/phones/nexus-s.json`:__ (sample snippet)
 +<pre>
 +{
 +  "additionalFeatures": "Contour Display, Near Field Communications (NFC), Three-axis gyroscope,
 +  Anti-fingerprint display coating, Internet Calling support (VoIP/SIP)",
 +  "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"
 +  }
 +}
 +</pre>
 +
 +__`test/unit/controllerSpec.js`:__
 +<pre>
 +...
 +    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'});
 +    });
 +...
 +</pre>
 +
 +__`test/e2e/scenarios.js`:__
 +<pre>
 +...
 +  describe('Phone detail view', function() {
 +
 +    beforeEach(function() {
 +      browser().navigateTo('../../app/index.html#/phones/nexus-s');
 +    });
 +
 +
 +    it('should display nexus-s page', function() {
 +      expect(binding('phone.name')).toBe('Nexus S');
 +    });
 +  });
 +...
 +</pre>
 +
 +## 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.
 +
 +<table id="tutorial_nav">
 +<tr>
 +<td id="previous_step">{@link tutorial.step_07 Previous}</td>
 +<td id="step_result">{@link  http://angular.github.com/angular-phonecat/step-8/app Live Demo
 +}</td>
 +<td id="tut_home">{@link tutorial Tutorial Home}</td>
 +<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-7...step-8 Code
 +Diff}</td>
 +<td id="next_step">{@link tutorial.step_09 Next}</td>
 +</tr>
 +</table>
 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
 +<table id="tutorial_nav">
 +<tr>
 +<td id="previous_step">{@link tutorial.step_08 Previous}</td>
 +<td id="step_result">{@link  http://angular.github.com/angular-phonecat/step-9/app Live Demo
 +}</td>
 +<td id="tut_home">{@link tutorial Tutorial Home}</td>
 +<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-8...step-9 Code
 +Diff}</td>
 +<td id="next_step">{@link tutorial.step_10 Next}</td>
 +</tr>
 +</table>
 +
 +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`:__
 +<pre>
 +...
 + <script src="lib/angular/angular.js" ng:autobind></script>
 + <script src="js/controllers.js"></script>
 + <script src="app/js/filters.js"></script>
 +...
 +</pre>
 +
 +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`:__
 +<pre>
 +<img ng:src="{{phone.images[0].large}}" class="phone"/>
 +<h1>{{phone.name}}</h1>
 +<p>{{phone.description}}</p>
 +...
 +<ul class="specs">
 +  ...
 +  <li>
 +    <span>Connectivity</span>
 +    <dl>
 +      <dt>Network Support</dt>
 +      <dd>{{phone.connectivity.cell}}</dd>
 +      <dt>WiFi</dt>
 +      <dd>{{phone.connectivity.wifi}}</dd>
 +      <dt>Bluetooth</dt>
 +      <dd>{{phone.connectivity.bluetooth}}</dd>
 +      <dt>Infrared</dt>
 +      <dd>{{phone.connectivity.infrared | checkmark}}</dd>
 +      <dt>GPS</dt>
 +      <dd>{{phone.connectivity.gps | checkmark}}</dd>
 +    </dl>
 +  </li>
 +...
 +</ul>
 +</pre>
 +
 +__`app/js/filters.js`:__ (New)
 +<pre>
 +angular.filter('checkmark', function(input) {
 +  return input ? '\u2713' : '\u2718';
 +});
 +</pre>
 +
 +__`test/unit/filtersSpec.js`:__ (New)
 +<pre>
 +describe('checkmark filter', function() {
 +
 +  it('should convert boolean values to unicode checkmark or cross', function() {
 +    expect(angular.filter.checkmark(true)).toBe('\u2713');
 +    expect(angular.filter.checkmark(false)).toBe('\u2718');
 +  });
 +})
 +</pre>
 +
 +## 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.
 +
 +<table id="tutorial_nav">
 +<tr>
 +<td id="previous_step">{@link tutorial.step_08 Previous}</td>
 +<td id="step_result">{@link  http://angular.github.com/angular-phonecat/step-9/app Live Demo
 +}</td>
 +<td id="tut_home">{@link tutorial Tutorial Home}</td>
 +<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-8...step-9 Code
 +Diff}</td>
 +<td id="next_step">{@link tutorial.step_10 Next}</td>
 +</tr>
 +</table>
 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 +<table id="tutorial_nav"> +<tr> +<td id="previous_step">{@link tutorial.step_09 Previous}</td> +<td id="step_result">{@link  http://angular.github.com/angular-phonecat/step-10/app Live Demo +}</td> +<td id="tut_home">{@link tutorial Tutorial Home}</td> +<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-9...step-10 +Code Diff}</td> +<td id="next_step">{@link tutorial.step_11 Next}</td> +</tr> +</table> + +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`:__ +<pre> +<img ng:src="{{mainImageUrl}}" class="phone"/> + +<h1>{{phone.name}}</h1> + +<p>{{phone.description}}</p> + +<ul class="phone-thumbs"> +  <li ng:repeat="img in phone.images"> +    <img ng:src="{{img}}" ng:click="setImage(img)"> +  </li> +</ul> +... +</pre> + +__`app/js/controllers.js`:__ +<pre> +... +function PhoneDetailCtrl($xhr) { +  var self = this; + +  $xhr('GET', 'phones/' + self.params.phoneId + '.json', function(code, response) { +    self.phone = response; +    self.mainImageUrl = response.images[0]; +  }); + +  self.setImage = function(imageUrl) { +    self.mainImageUrl = imageUrl; +  } +} + +//PhoneDetailCtrl.$inject = ['$xhr']; +</pre> + +__`test/e2e/scenarios.js`:__ +<pre> +/* 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'); +    }); +  }); +}); +</pre> + +## 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. + + +<table id="tutorial_nav"> +<tr> +<td id="previous_step">{@link tutorial.step_09 Previous}</td> +<td id="step_result">{@link  http://angular.github.com/angular-phonecat/step-10/app Live Demo +}</td> +<td id="tut_home">{@link tutorial Tutorial Home}</td> +<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-9...step-10 +Code Diff}</td> +<td id="next_step">{@link tutorial.step_11 Next}</td> +</tr> +</table> 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
 +<table id="tutorial_nav">
 +<tr>
 +<td id="previous_step">{@link tutorial.step_10 Previous}</td>
 +<td id="step_result">{@link  http://angular.github.com/angular-phonecat/step-11/app Live Demo
 +}</td>
 +<td id="tut_home">{@link tutorial Tutorial Home}</td>
 +<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-10...step-11
 +Code Diff}</td>
 +<td id="next_step">Next</td>
 +</tr>
 +</table>
 +
 +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`.__
 +<pre>
 +...
 +  <script src="js/services.js"></script>
 +...
 +</pre>
 +
 +
 +__`app/js/services.js`.__ (New)
 +<pre>
 + angular.service('Phone', function($resource){
 +  return $resource('phones/:phoneId.json', {}, {
 +    query: {method:'GET', params:{phoneId:'phones'}, isArray:true}
 +  });
 + });
 +</pre>
 +
 +__`app/js/controllers.js`.__
 +<pre>
 +...
 +
 +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'];
 +</pre>
 +
 +__`test/unit/controllersSpec.js`:__
 +<pre>
 +/* 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'});
 +    });
 +  });
 +});
 +</pre>
 +
 +
 +## 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.
 +
 +<table id="tutorial_nav">
 +<tr>
 +<td id="previous_step">{@link tutorial.step_10 Previous}</td>
 +<td id="step_result">{@link  http://angular.github.com/angular-phonecat/step-11/app Live Demo
 +}</td>
 +<td id="tut_home">{@link tutorial Tutorial Home}</td>
 +<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-10...step-11
 +Code Diff}</td>
 +<td id="next_step">Next</td>
 +</tr>
 +</table>
 | 
