diff options
| author | Misko Hevery | 2011-04-29 15:18:27 -0700 |
|---|---|---|
| committer | Igor Minar | 2011-06-06 22:28:38 -0700 |
| commit | 11e9572b952e49b01035e956c412d6095533031a (patch) | |
| tree | 04dbf96802f552693d44c541c0d825a2769e3d57 /docs/content | |
| parent | b6bc6c2ddf1ae1523ec7e4cb92db209cd6501181 (diff) | |
| download | angular.js-11e9572b952e49b01035e956c412d6095533031a.tar.bz2 | |
Move documentation under individual headings
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>
|
