From b842642b574a2b95c53b791308ed1bf8ff9d304d Mon Sep 17 00:00:00 2001 From: Igor Minar Date: Wed, 15 Jun 2011 22:31:40 -0700 Subject: docs - stripping extra new lines --- docs/content/api/angular.service.ngdoc | 3 - docs/content/api/index.ngdoc | 26 ----- docs/content/cookbook/advancedform.ngdoc | 11 --- docs/content/cookbook/buzz.ngdoc | 2 - docs/content/cookbook/deeplinking.ngdoc | 20 ---- docs/content/cookbook/form.ngdoc | 13 --- docs/content/cookbook/helloworld.ngdoc | 4 - docs/content/cookbook/index.ngdoc | 23 ----- docs/content/cookbook/mvc.ngdoc | 9 -- .../guide/dev_guide.bootstrap.auto_bootstrap.ngdoc | 29 ------ .../dev_guide.bootstrap.manual_bootstrap.ngdoc | 10 -- docs/content/guide/dev_guide.bootstrap.ngdoc | 21 ---- ...e.compiler.directives.creating_directives.ngdoc | 10 -- .../guide/dev_guide.compiler.directives.ngdoc | 14 --- .../dev_guide.compiler.directives_widgets.ngdoc | 11 --- .../dev_guide.compiler.extending_compiler.ngdoc | 11 --- docs/content/guide/dev_guide.compiler.markup.ngdoc | 24 ----- docs/content/guide/dev_guide.compiler.ngdoc | 8 -- .../dev_guide.compiler.testing_dom_element.ngdoc | 7 -- ...dev_guide.compiler.understanding_compiler.ngdoc | 19 ---- ...v_guide.compiler.widgets.creating_widgets.ngdoc | 20 ---- .../content/guide/dev_guide.compiler.widgets.ngdoc | 11 --- docs/content/guide/dev_guide.di.ngdoc | 10 -- .../guide/dev_guide.di.understanding_di.ngdoc | 29 ------ .../guide/dev_guide.di.using_di_controllers.ngdoc | 12 --- docs/content/guide/dev_guide.expressions.ngdoc | 49 ---------- docs/content/guide/dev_guide.introduction.ngdoc | 10 -- docs/content/guide/dev_guide.mvc.ngdoc | 8 -- .../dev_guide.mvc.understanding_controller.ngdoc | 67 +------------ .../guide/dev_guide.mvc.understanding_model.ngdoc | 24 ----- .../guide/dev_guide.mvc.understanding_view.ngdoc | 6 -- docs/content/guide/dev_guide.overview.ngdoc | 50 ---------- .../dev_guide.scopes.controlling_scopes.ngdoc | 11 --- docs/content/guide/dev_guide.scopes.ngdoc | 13 --- .../dev_guide.scopes.understanding_scopes.ngdoc | 14 --- .../guide/dev_guide.scopes.updating_scopes.ngdoc | 9 -- .../guide/dev_guide.scopes.working_scopes.ngdoc | 13 --- .../dev_guide.services.creating_services.ngdoc | 12 --- .../dev_guide.services.injecting_controllers.ngdoc | 12 --- .../dev_guide.services.managing_dependencies.ngdoc | 14 --- docs/content/guide/dev_guide.services.ngdoc | 6 -- .../dev_guide.services.registering_services.ngdoc | 16 --- .../dev_guide.services.testing_services.ngdoc | 14 --- ...dev_guide.services.understanding_services.ngdoc | 10 -- docs/content/guide/dev_guide.templates.css.ngdoc | 18 ---- .../guide/dev_guide.templates.databinding.ngdoc | 9 -- ..._guide.templates.filters.creating_filters.ngdoc | 11 --- .../guide/dev_guide.templates.filters.ngdoc | 10 -- ...dev_guide.templates.filters.using_filters.ngdoc | 14 --- ....templates.formatters.creating_formatters.ngdoc | 8 -- .../guide/dev_guide.templates.formatters.ngdoc | 6 -- ...ide.templates.formatters.using_formatters.ngdoc | 3 - docs/content/guide/dev_guide.templates.ngdoc | 12 --- ....templates.validators.creating_validators.ngdoc | 18 ---- .../guide/dev_guide.templates.validators.ngdoc | 29 ------ docs/content/guide/dev_guide.unit-testing.ngdoc | 36 ------- docs/content/guide/index.ngdoc | 19 ---- docs/content/misc/contribute.ngdoc | 108 --------------------- docs/content/misc/downloading.ngdoc | 16 --- docs/content/misc/faq.ngdoc | 29 ------ docs/content/misc/started.ngdoc | 46 --------- docs/content/tutorial/index.ngdoc | 24 ----- docs/content/tutorial/step_00.ngdoc | 42 +------- docs/content/tutorial/step_01.ngdoc | 20 ---- docs/content/tutorial/step_02.ngdoc | 64 ------------ docs/content/tutorial/step_03.ngdoc | 56 ----------- docs/content/tutorial/step_04.ngdoc | 60 ------------ docs/content/tutorial/step_05.ngdoc | 66 ------------- docs/content/tutorial/step_06.ngdoc | 30 ------ docs/content/tutorial/step_07.ngdoc | 61 ------------ docs/content/tutorial/step_08.ngdoc | 54 ----------- docs/content/tutorial/step_09.ngdoc | 28 ------ docs/content/tutorial/step_10.ngdoc | 48 --------- docs/content/tutorial/step_11.ngdoc | 60 ------------ docs/content/tutorial/the_end.ngdoc | 6 -- 75 files changed, 3 insertions(+), 1723 deletions(-) (limited to 'docs/content') diff --git a/docs/content/api/angular.service.ngdoc b/docs/content/api/angular.service.ngdoc index 9a191921..1685f581 100644 --- a/docs/content/api/angular.service.ngdoc +++ b/docs/content/api/angular.service.ngdoc @@ -3,11 +3,9 @@ @name angular.service @description - The services API provides objects for carrying out common web app tasks. Service objects are managed by angular's {@link guide/dev_guide.di dependency injection system}. - * {@link angular.service.$browser $browser } - Provides an instance of a browser object * {@link angular.service.$cookieStore $cookieStore } - Provides key / value storage backed by session cookies @@ -27,6 +25,5 @@ server-side data sources * {@link angular.service.$window $window } - References the browsers `window` object * {@link angular.service.$xhr $xhr} - Generates an XHR request. - For information on how angular services work and how to write your own services, see {@link guide/dev_guide.services Angular Services} in the angular Developer Guide. diff --git a/docs/content/api/index.ngdoc b/docs/content/api/index.ngdoc index d604fd17..05928ab4 100644 --- a/docs/content/api/index.ngdoc +++ b/docs/content/api/index.ngdoc @@ -2,10 +2,8 @@ @name API Reference @description - ## Angular Compiler API - * {@link angular.widget Widgets} - Angular custom DOM element * {@link angular.directive Directives} - Angular DOM element attributes * {@link angular.markup Markup} and {@link angular.attrMarkup Attribute Markup} @@ -14,47 +12,33 @@ * {@link angular.validator Validators} - Angular input validators * {@link angular.compile angular.compile()} - Template compiler - ## Angular Scope API - * {@link angular.scope Scope Object} - Angular scope object - - ## Angular Services & Dependency Injection API - * {@link angular.service Angular Services} * {@link angular.injector angular.injector() } - - ## Angular Testing API - * {@link angular.mock Testing Mocks API} - Mock objects for testing * {@link https://docs.google.com/document/d/11L8htLKrh6c92foV71ytYpiKkeKpM4_a5-9c3HywfIc/edit?hl=en_US Angular Scenario Runner} - Automated scenario testing documentation - - ## Angular Utility Functions - ### HTML & DOM Manipulation - * {@link angular.element angular.element()} - ### Misc - * {@link angular.bind angular.bind() } * {@link angular.extend angular.extend() } * {@link angular.forEach angular.forEach() } @@ -62,11 +46,8 @@ Angular Scenario Runner} - Automated scenario testing documentation * {@link angular.noop angular.noop() } - - ## Type Identification - * {@link angular.isArray angular.isArray() } * {@link angular.isDate angular.isDate() } * {@link angular.isDefined angular.isDefined() } @@ -76,25 +57,18 @@ Angular Scenario Runner} - Automated scenario testing documentation * {@link angular.isString angular.isString() } * {@link angular.isUndefined angular.isUndefined() } - ## Strings - * {@link angular.lowercase angular.lowercase() } * {@link angular.uppercase angular.uppercase() } - ### JSON - * {@link angular.fromJson angular.fromJson() } * {@link angular.toJson angular.toJson() } - - - ## Utility methods for JavaScript types * {@link angular.Object Object API} - Utility functions for JavaScript objects * {@link angular.Array Array API} - Utility functions for JavaScript arrays diff --git a/docs/content/cookbook/advancedform.ngdoc b/docs/content/cookbook/advancedform.ngdoc index 747e8891..bbf4875e 100644 --- a/docs/content/cookbook/advancedform.ngdoc +++ b/docs/content/cookbook/advancedform.ngdoc @@ -3,11 +3,9 @@ @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. -
-


-

, @@ -58,7 +52,6 @@ detection, and preventing invalid form submission.

- [ add ]
@@ -75,7 +68,6 @@ ng:validate="regexp:zip"/>

-
Debug View:
form={{form}}
@@ -104,11 +96,8 @@ master.$equals(form)}}">Save
 
 
 
-
-
 #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.
diff --git a/docs/content/cookbook/buzz.ngdoc b/docs/content/cookbook/buzz.ngdoc
index 5b83de79..974a7c74 100644
--- a/docs/content/cookbook/buzz.ngdoc
+++ b/docs/content/cookbook/buzz.ngdoc
@@ -3,7 +3,6 @@
 @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
@@ -11,7 +10,6 @@ attach behavior to them. For example you can use the
 API}
 to retrieve Buzz activity and comments.
 
-
 
  
     
   
-


-

, @@ -38,7 +33,6 @@ allow a user to enter data.

- [ add ]
@@ -56,7 +50,6 @@ ng:validate="regexp:zip"/>

user={{user}}
- it('should show debug', function(){ @@ -69,13 +62,11 @@ ng:validate="regexp:zip"/>

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/); @@ -84,7 +75,6 @@ ng:validate="regexp:zip"/>

.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/); @@ -96,11 +86,8 @@ ng:validate="regexp:zip"/>

- - # Things to notice - * The user data model is initialized {@link api/angular.directive.ng:controller controller} and is available in the {@link api/angular.scope scope} with the initial data. diff --git a/docs/content/cookbook/helloworld.ngdoc b/docs/content/cookbook/helloworld.ngdoc index a2557b5d..8018a399 100644 --- a/docs/content/cookbook/helloworld.ngdoc +++ b/docs/content/cookbook/helloworld.ngdoc @@ -3,7 +3,6 @@ @name Cookbook: Hello World @description - Your name: @@ -19,13 +18,10 @@
- # Things to notice - Take a look through the source and note: - * The script tag that {@link guide/dev_guide.bootstrap bootstraps} the angular environment. * The text {@link api/angular.widget.HTML input widget} which is bound to the greeting name text. * No need for listener registration and event firing on change events. diff --git a/docs/content/cookbook/index.ngdoc b/docs/content/cookbook/index.ngdoc index e8a3d7d7..c3f9d8bf 100644 --- a/docs/content/cookbook/index.ngdoc +++ b/docs/content/cookbook/index.ngdoc @@ -3,68 +3,48 @@ @name Cookbook @description - Welcome to the angular cookbook. Here we will show you typical uses of angular by example. - - # Hello World - {@link helloworld Hello World}: The simplest possible application that demonstrates the classic Hello World! - - # Basic Form - {@link 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 advancedform 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 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 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 api/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 @@ -72,11 +52,8 @@ order of initialization. This safeguards you from the perils of global state (a implement long lived objects). - - # External Resources - {@link 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 index 04feffcc..6a167469 100644 --- a/docs/content/cookbook/mvc.ngdoc +++ b/docs/content/cookbook/mvc.ngdoc @@ -3,19 +3,15 @@ @name Cookbook: MVC @description - MVC allows for a clean an testable separation between the behavior (controller) and the view (HTML template). A Controller is just a JavaScript class which is grafted onto the scope of the view. This makes it very easy for the controller and the view to share the model. - The model is simply the controller's this. This makes it very easy to test the controller in isolation since one can simply instantiate the controller and test without a view, because there is no connection between the controller and the view. - - -

Tic-Tac-Toe

Next Player: {{nextMove}} @@ -109,7 +104,6 @@ no connection between the controller and the view. expect(element('.winner').text()).toEqual('Player X has won!'); }); - function piece(row, col) { element('.board tr:nth-child('+row+') td:nth-child('+col+')').click(); } @@ -117,11 +111,8 @@ no connection between the controller and the view. - - # Things to notice - * The controller is defined in JavaScript and has no reference to the rendering logic. * The controller is instantiated by and injected into the view. * The controller can be instantiated in isolation (without a view) and the code will still execute. diff --git a/docs/content/guide/dev_guide.bootstrap.auto_bootstrap.ngdoc b/docs/content/guide/dev_guide.bootstrap.auto_bootstrap.ngdoc index 1c5e3e26..0b03e19c 100644 --- a/docs/content/guide/dev_guide.bootstrap.auto_bootstrap.ngdoc +++ b/docs/content/guide/dev_guide.bootstrap.auto_bootstrap.ngdoc @@ -3,30 +3,23 @@ @name Developer Guide: Initializing Angular: Automatic Initiialization @description - Angular initializes automatically when you load the angular script into your page, specifying angular's `ng:autobind` attribute with no arguments: - ` - From the `name` attribute of the `` tags, angular automatically sets up two-way data binding, and we also demonstrate some easy input validation: - Quantity: Cost: - These input widgets look normal enough, but consider these points: - * 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" component of the Model-View-Controller design pattern. @@ -131,13 +107,10 @@ or leave the the input fields blank, the borders turn red color, and the display These `ng:` directives make it easier to implement field validators than coding them in JavaScript, no? Yes. - And finally, the mysterious `{{ double curly braces }}`: - Total: {{qty * cost | currency}} - This notation, `{{ _expression_ }}`, is a bit of built-in angular {@link dev_guide.compiler.markup markup}, a shortcut for displaying data to the user. The expression within curly braces gets transformed by the angular compiler into an angular directive ({@link api/angular.directive.ng:bind @@ -145,36 +118,28 @@ ng:bind}). The expression itself can be a combination of both an expression and dev_guide.templates.filters filter}: `{{ expression | filter }}`. Angular provides filters for formatting display data. - In the example above, the expression in double-curly braces directs angular to, "Bind the data we got from the input widgets to the display, multiply them together, and format the resulting number into output that looks like money." - - # The Angular Philosophy - Angular is built around the belief that declarative code is better than imperative when it comes to building UIs and wiring software components together, while imperative code is excellent 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 so by simply adding text to the HTML template, saving the code, and refreshing your browser: -
 Hello
 
- Or, as in programmatic systems (like {@link http://code.google.com/webtoolkit/ GWT}), you would have to write the code and then run the code like this: -
 var label = new Label();
 label.setText('Hello');
@@ -182,15 +147,11 @@ label.setClass('label');
 parent.addChild(label);
 
- That's one line 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. @@ -201,11 +162,9 @@ development work to progress in parallel, and allows for reuse of both sides. 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 that angular is not: - * 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, @@ -225,14 +184,10 @@ changes to the model are automatically reflected in the view. Any changes by the are automatically reflected in the model. - - # 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 @@ -256,15 +211,11 @@ get started developing features quickly. As a bonus, you get full control over t process in automated tests. - - # 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. - @@ -274,7 +225,6 @@ the talk was given in July of 2010. allowfullscreen="true" width="480" height="385"> - {@link https://docs.google.com/present/edit?id=0Abz6S2TvsDWSZDQ0OWdjaF8yNTRnODczazdmZg&hl=en&authkey=CO-b7oID diff --git a/docs/content/guide/dev_guide.scopes.controlling_scopes.ngdoc b/docs/content/guide/dev_guide.scopes.controlling_scopes.ngdoc index ca63cbc3..cdbad444 100644 --- a/docs/content/guide/dev_guide.scopes.controlling_scopes.ngdoc +++ b/docs/content/guide/dev_guide.scopes.controlling_scopes.ngdoc @@ -3,48 +3,37 @@ @name Developer Guide: Scopes: Applying Controllers to Scopes @description - When a controller function is applied to a scope, the scope is augmented with the behavior defined in the controller. The end result is that the scope behaves as if it were the controller: -
 var scope = angular.scope();
 scope.salutation = 'Hello';
 scope.name = 'World';
 
-
 expect(scope.greeting).toEqual(undefined);
 
-
 scope.$watch('name', function(){
 this.greeting = this.salutation + ' ' + this.name + '!';
 });
 
-
 expect(scope.greeting).toEqual('Hello World!');
 scope.name = 'Misko';
 // scope.$eval() will propagate the change to listeners
 expect(scope.greeting).toEqual('Hello World!');
 
-
 scope.$eval();
 expect(scope.greeting).toEqual('Hello Misko!');
 
- - ## Related Topics - * {@link dev_guide.scopes Angular Scope Objects} * {@link dev_guide.scopes.understanding_scopes Understanding Angular Scopes} * {@link dev_guide.scopes.working_scopes Working With Angular Scopes} * {@link dev_guide.scopes.updating_scopes Updating Angular Scopes} - ## Related API - * {@link api/angular.scope Angular Scope API} diff --git a/docs/content/guide/dev_guide.scopes.ngdoc b/docs/content/guide/dev_guide.scopes.ngdoc index 730ac348..e9706e2f 100644 --- a/docs/content/guide/dev_guide.scopes.ngdoc +++ b/docs/content/guide/dev_guide.scopes.ngdoc @@ -4,48 +4,35 @@ @description - - An angular scope is a JavaScript type defined by angular. Instances of this type are objects that serve as the context within which all model and controller methods live and get evaluated. - Angular links scope objects to specific points in a compiled (processed) template. This linkage provides the contexts in which angular creates data-bindings between the model and the view. You can think of angular scope objects as the medium through which the model, view, and controller communicate. - In addition to providing the context in which data is evaluated, angular scope objects watch for model changes. The scope objects also notify all components interested in any model changes (for example, functions registered through {@link api/angular.scope.$watch $watch}, bindings created by {@link api/angular.directive.ng:bind ng:bind}, or HTML input elements). - Angular scope objects are responsible for: - * Gluing the model, controller and view template together. * Providing the mechanism to watch for model changes ({@link api/angular.scope.$watch}). * Notifying interested components when the model changes ({@link api/angular.scope.$eval}). * Providing the context in which all controller functions and angular expressions are evaluated. - - ## Related Topics - * {@link dev_guide.scopes.understanding_scopes Understanding Scopes} * {@link dev_guide.scopes.working_scopes Working With Scopes} * {@link dev_guide.scopes.controlling_scopes Applying Controllers to Scopes} * {@link dev_guide.scopes.updating_scopes Updating Scopes} - ## Related API - * {@link api/angular.scope Angular Scope API} - - diff --git a/docs/content/guide/dev_guide.scopes.understanding_scopes.ngdoc b/docs/content/guide/dev_guide.scopes.understanding_scopes.ngdoc index 073c919e..704c9241 100644 --- a/docs/content/guide/dev_guide.scopes.understanding_scopes.ngdoc +++ b/docs/content/guide/dev_guide.scopes.understanding_scopes.ngdoc @@ -3,7 +3,6 @@ @name Developer Guide: Scopes: Understanding Scopes @description - Angular automatically creates a root scope during initialization, and attaches it to the page's root DOM element (usually ``). The root scope object, along with any of its child scope objects, serves as the infrastructure on which your data model is built. The data model (JavaScript @@ -11,20 +10,16 @@ objects, arrays, or primitives) is attached to angular scope properties. Angular values to the DOM where bindings are specified in the template. Angular attaches any controller functions you have created to their respective scope objects. - - Angular scopes can be nested, so a child scope has a parent scope upstream in the DOM. When you display an angular expression in the view, angular walks the DOM tree looking in the closest attached scope object for the specified data. If it doesn't find the data in the closest attached scope, it looks further up the scope hierarchy until it finds the data. - A child scope object inherits properties from its parents. For example, in the following snippet of code, observe how the value of `name` changes, based on the HTML element it is displayed in: -
    @@ -41,7 +36,6 @@ code, observe how the value of `name` changes, based on the HTML element it is d expect(using('.doc-example-live').repeater('li').row(1)). toEqual(['Misko']); - expect(using('.doc-example-live').repeater('li').row(2)). toEqual(['Gail']); expect(using('.doc-example-live').repeater('li').row(3)). @@ -52,32 +46,24 @@ code, observe how the value of `name` changes, based on the HTML element it is d - The angular {@link api/angular.widget.@ng:repeat ng:repeat} directive creates a new scope for each element that it repeats (in this example the elements are list items). In the `
      ` element, we initialized `name` to "Hank", and we created an array called `names` to use as the data source for the list items. In each `
    • ` element, `name` is overridden. Outside of the `
    • ` repeater, the original value of `name` is displayed. - The following illustration shows the DOM and angular scopes for the example above: - - - ## Related Topics - * {@link dev_guide.scopes Angular Scope Objects} * {@link dev_guide.scopes.working_scopes Working With Scopes} * {@link dev_guide.scopes.controlling_scopes Applying Controllers to Scopes} * {@link dev_guide.scopes.updating_scopes Updating Scopes} - ## Related API - * {@link api/angular.scope Angular Scope API} diff --git a/docs/content/guide/dev_guide.scopes.updating_scopes.ngdoc b/docs/content/guide/dev_guide.scopes.updating_scopes.ngdoc index ff6734cc..2d5f1725 100644 --- a/docs/content/guide/dev_guide.scopes.updating_scopes.ngdoc +++ b/docs/content/guide/dev_guide.scopes.updating_scopes.ngdoc @@ -3,19 +3,16 @@ @name Developer Guide: Scopes: Updating Scope Properties @description - You can update a scope by calling its {@link api/angular.scope.$eval $eval()} method, but usually you do not have to do this explicitly. In most cases, angular intercepts all external events (such as user interactions, XHRs, and timers) and calls the `$eval()` method on the scope object for you at the right time. The only time you might need to call `$eval()` explicitly is when you create your own custom widget or service. - The reason it is unnecessary to call `$eval()` from within your controller functions when you use built-in angular widgets and services is because a change in the data model triggers a call to the `$eval()` method on the scope object where the data model changed. - When a user inputs data, angularized widgets copy the data to the appropriate scope and then call the `$eval()` method on the root scope to update the view. It works this way because scopes are inherited, and a child scope `$eval()` overrides its parent's `$eval()` method. Updating the whole @@ -23,25 +20,19 @@ page requires a call to `$eval()` on the root scope as `$root.$eval()`. Similarl to fetch data from a server is made and the response comes back, the data is written into the model and then `$eval()` is called to push updates through to the view and any other dependents. - A widget that creates scopes (such as {@link api/angular.widget.@ng:repeat ng:repeat}) is responsible for forwarding `$eval()` calls from the parent to those child scopes. That way, calling `$eval()` on the root scope will update the whole page. This creates a spreadsheet-like behavior for your app; the bound views update immediately as the user enters data. - - ## Related Documents - * {@link dev_guide.scopes Angular Scope Objects} * {@link dev_guide.scopes.understanding_scopes Understanding Angular Scope Objects} * {@link dev_guide.scopes.working_scopes Working With Angular Scopes} * {@link dev_guide.scopes.controlling_scopes Applying Controllers to Scopes} - ## Related API - * {@link api/angular.scope Angular Scope API} diff --git a/docs/content/guide/dev_guide.scopes.working_scopes.ngdoc b/docs/content/guide/dev_guide.scopes.working_scopes.ngdoc index ab507e16..8e4503a5 100644 --- a/docs/content/guide/dev_guide.scopes.working_scopes.ngdoc +++ b/docs/content/guide/dev_guide.scopes.working_scopes.ngdoc @@ -3,27 +3,22 @@ @name Developer Guide: Scopes: Working With Angular Scopes @description - When you use {@link api/angular.directive.ng:autobind ng:autobind} to bootstrap your application, angular creates the root scope automatically for you. If you need more control over the bootstrapping process, or if you need to create a root scope for a test, you can do so using the {@link api/angular.scope angular.scope()} API. - Here is a simple code snippet that demonstrates how to create a scope object, assign model properties to it, and register listeners to watch for changes to the model properties: -
       var scope = angular.scope();
       scope.salutation = 'Hello';
       scope.name = 'World';
       
      -
       // Verify that greeting is undefined
       expect(scope.greeting).toEqual(undefined);
       
      -
       // Set up the watcher...
       scope.$watch('name', function(){
       // when 'name' changes, set 'greeting'...
      @@ -31,35 +26,27 @@ this.greeting = this.salutation + ' ' + this.name + '!';
       }
       );
       
      -
       // verify that 'greeting' was set...
       expect(scope.greeting).toEqual('Hello World!');
       
      -
       // 'name' changed!
       scope.name = 'Misko';
       
      -
       // scope.$eval() will propagate the change to listeners
       expect(scope.greeting).toEqual('Hello World!');
       
      -
       scope.$eval();
       // verify that '$eval' propagated the change
       expect(scope.greeting).toEqual('Hello Misko!');
       
      - ## Related Topics - * {@link dev_guide.scopes Angular Scope Objects} * {@link dev_guide.scopes.understanding_scopes Understanding Scopes} * {@link dev_guide.scopes.controlling_scopes Applying Controllers to Scopes} * {@link dev_guide.scopes.updating_scopes Updating Scopes} - ## Related API - * {@link api/angular.scope Angular Scope API} diff --git a/docs/content/guide/dev_guide.services.creating_services.ngdoc b/docs/content/guide/dev_guide.services.creating_services.ngdoc index d36c9d67..b75e75a3 100644 --- a/docs/content/guide/dev_guide.services.creating_services.ngdoc +++ b/docs/content/guide/dev_guide.services.creating_services.ngdoc @@ -3,15 +3,12 @@ @name Developer Guide: Angular Services: Creating Angular Services @description - While angular offers several useful services, for any nontrivial application you'll find it useful to write your own custom services. To do this you begin by registering a service factory function that angular's DI will use to create the service object when it is needed. - The `angular.service` method accepts three parameters: - - `{string} name` - Name of the service. - `{function()} factory` - Factory function (called just once by DI). - `{Object} config` - Configuration object with the following properties: @@ -22,23 +19,19 @@ array. Defaults to `[]`. instantiated when angular boots. If false, the 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. - All angular services participate in {@link dev_guide.di 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. - Following is an example of a very simple service. This service depends on the `$window` service (which is passed as a parameter to the factory function) and is just a function. The service simply stores all notifications; after the third one, the service displays all of the notifications by window alert. -
           angular.service('notify', function(win) {
             var msgs = [];
      @@ -53,19 +46,14 @@ window alert.
       
      - - ## Related Topics - * {@link dev_guide.services.understanding_services Understanding Angular Services} * {@link dev_guide.services.registering_services Registering Angular Services} * {@link dev_guide.services.managing_dependencies Managing Service Dependencies} * {@link dev_guide.services.injecting_controllers Injecting Services Into Controllers } * {@link dev_guide.services.testing_services Testing Angular Services} - ## Related API - * {@link api/angular.service Angular Service API} diff --git a/docs/content/guide/dev_guide.services.injecting_controllers.ngdoc b/docs/content/guide/dev_guide.services.injecting_controllers.ngdoc index 75630b32..0046dd7f 100644 --- a/docs/content/guide/dev_guide.services.injecting_controllers.ngdoc +++ b/docs/content/guide/dev_guide.services.injecting_controllers.ngdoc @@ -3,11 +3,9 @@ @name Developer Guide: Angular Services: Injecting Services Into Controllers @description - Using services as dependencies for controllers is very similar to using services as dependencies for another service. - Since JavaScript is a dynamic language, DI can't figure out which services to inject by static types (like in static typed languages). Therefore, you must specify the service name by using the `$inject` property, which is an array containing strings with names of services to be injected. @@ -16,7 +14,6 @@ IDs matters: the order of the services in the array will be used when calling th with injected parameters. The names of parameters in factory function don't matter, but by convention they match the service IDs. -
       function myController($loc, $log) {
       this.firstMethod = function() {
      @@ -32,7 +29,6 @@ this.secondMethod = function() {
       myController.$inject = ['$location', '$log'];
       
      - -

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

      @@ -73,19 +66,14 @@ it('should test service', function(){ - - ## Related Topics - {@link dev_guide.services.understanding_services Understanding Angular Services} {@link dev_guide.services.creating_services Creating Angular Services} {@link dev_guide.services.registering_services Registering Angular Services} {@link dev_guide.services.managing_dependencies Managing Service Dependencies} {@link dev_guide.services.testing_services Testing Angular Services} - ## Related API - {@link api/angular.service Angular Service API} diff --git a/docs/content/guide/dev_guide.services.managing_dependencies.ngdoc b/docs/content/guide/dev_guide.services.managing_dependencies.ngdoc index 5adbefae..5f45b001 100644 --- a/docs/content/guide/dev_guide.services.managing_dependencies.ngdoc +++ b/docs/content/guide/dev_guide.services.managing_dependencies.ngdoc @@ -3,20 +3,16 @@ @name Developer Guide: Angular Services: Managing Service Dependencies @description - Angular allows services to declare other services as dependencies needed for construction of their instances. - To declare dependencies, you specify them in the factory function signature and via the `$inject` property, as an array of string identifiers. Optionally the `$inject` property declaration can be dropped (see "Inferring `$inject`" but note that that is currently an experimental feature). - Here is an example of two services that depend on each other, as well as on other services that are provided by angular's web framework: -
       /**
       * batchLog service allows for messages to be queued in memory and flushed
      @@ -27,7 +23,6 @@ provided by angular's web framework:
       angular.service('batchLog', function($defer, $log) {
       var messageQueue = [];
       
      -
       function log() {
        if (messageQueue.length) {
          $log('batchLog messages: ', messageQueue);
      @@ -36,14 +31,12 @@ function log() {
        $defer(log, 50000);
        }
       
      -
       return function(message) {
        messageQueue.push(message);
       }
       }, {$inject: ['$defer', '$log']);
       // note how we declared dependency on built-in $defer and $log services above
       
      -
       /**
       * routeTemplateMonitor monitors each $route change and logs the current
       * template via the batchLog service.
      @@ -55,10 +48,8 @@ $route.onChange(function() {
       }, {$inject: ['$route', 'batchLog'], $eager: true});
       
      - Things to notice in this example: - * The `batchLog` service depends on the built-in {@link api/angular.service.$defer $defer} and {@link api/angular.service.$log $log} services, and allows messages to be logged into the `console.log` in batches. @@ -77,20 +68,15 @@ this array with IDs and their order that the injector uses to determine which se order to inject. - - ## Related Topics - * {@link dev_guide.services.understanding_services Understanding Angular Services} * {@link dev_guide.services.creating_services Creating Angular Services} * {@link dev_guide.services.registering_services Registering Services} * {@link dev_guide.services.injecting_controllers Injecting Services Into Controllers } * {@link dev_guide.services.testing_services Testing Angular Services} - ## Related API - * {@link api/angular.service Angular Service API} * {@link api/angular.injector Angular Injector API} diff --git a/docs/content/guide/dev_guide.services.ngdoc b/docs/content/guide/dev_guide.services.ngdoc index 57449edc..01f747f7 100644 --- a/docs/content/guide/dev_guide.services.ngdoc +++ b/docs/content/guide/dev_guide.services.ngdoc @@ -3,18 +3,14 @@ @name Developer Guide: Angular Services @description - Services are a feature that angular brings to client-side web apps from the server side, where services have been commonly used for a long time. Services in angular apps are substitutable objects that are wired together using {@link dev_guide.di dependency injection (DI)}. Services are most often used with {@link dev_guide.di dependency injection}, also a key feature of angular apps. - - ## Related Topics - * {@link dev_guide.services.understanding_services Understanding Angular Services} * {@link dev_guide.services.creating_services Creating Angular Services} * {@link dev_guide.services.registering_services Registering Angular Services} @@ -22,8 +18,6 @@ most often used with {@link dev_guide.di dependency injection}, also a key featu * {@link dev_guide.services.injecting_controllers Injecting Services Into Conrollers} * {@link dev_guide.services.testing_services Testing Angular Services} - ## Related API - * {@link api/angular.service Angular Service API} diff --git a/docs/content/guide/dev_guide.services.registering_services.ngdoc b/docs/content/guide/dev_guide.services.registering_services.ngdoc index ea182944..cc50d678 100644 --- a/docs/content/guide/dev_guide.services.registering_services.ngdoc +++ b/docs/content/guide/dev_guide.services.registering_services.ngdoc @@ -3,12 +3,10 @@ @name Developer Guide: Angular Services: Registering Angular Services @description - To register a service, register a factory function that creates the service with angular's Injector. The Injector is exposed as {@link api/angular.scope.$service scope.$service}. The following pseudo-code shows a simple service registration: -
       angular.service('service id', function() {
         var shinyNewServiceInstance;
      @@ -17,27 +15,21 @@ angular.service('service id', function() {
       });
       
      - Note that you are not registering a service instance, but rather a factory function that will create this instance when called. - # Instantiating Angular Services - A service can be instantiated eagerly or lazily. By default angular instantiates services lazily, which means that a service will be created only when it is needed for instantiation of a service or an application component that depends on it. In other words, angular won't instantiate lazy services unless they are requested directly or indirectly by the application. - Eager services on the other hand, are instantiated right after the injector itself is created, which happens when the angular {@link dev_guide.bootstrap application initializes}. - To override the default, you can request that a service is eagerly instantiated as follows: -
       angular.service('service id', function() {
         var shinyNewServiceInstance;
      @@ -46,12 +38,10 @@ angular.service('service id', function() {
       }, {$eager: true});
       
      - While it is tempting to declare services as eager, only in few cases it is actually useful. If you are unsure whether to make a service eager, it likely doesn't need to be. To be more specific, a service should be declared as eager only if it fits one of these scenarios: - * Nothing in your application declares this service as its dependency, and this service affects the state or configuration of the application (e.g. a service that configures `$route` or `$resource` services) @@ -60,7 +50,6 @@ because the service passively observes the application and it is optional for ot components to depend on it. An example of this scenario is a service that monitors and logs application memory usage. - Lastly, it is important to realize that all angular services are applicaiton singletons. This means that there is only one instance of a given service per injector. Since angular is lethally allergic to the global state, it is possible to create multiple injectors, each with its own instance of a @@ -68,19 +57,14 @@ given service, but that is rarely needed, except in tests where this property is important. - - ## Related Topics - * {@link dev_guide.services.understanding_services Understanding Angular Services} * {@link dev_guide.services.creating_services Creating Angular Services} * {@link dev_guide.services.managing_dependencies Managing Service Dependencies} * {@link dev_guide.services.injecting_controllers Injecting Services Into Controllers } * {@link dev_guide.services.testing_services Testing Angular Services} - ## Related API - * {@link api/angular.service Angular Service API} diff --git a/docs/content/guide/dev_guide.services.testing_services.ngdoc b/docs/content/guide/dev_guide.services.testing_services.ngdoc index 65e1ab6d..bc860364 100644 --- a/docs/content/guide/dev_guide.services.testing_services.ngdoc +++ b/docs/content/guide/dev_guide.services.testing_services.ngdoc @@ -3,29 +3,24 @@ @name Developer Guide: Angular Services: Testing Angular Services @description - Following is a unit test for the service in the example in {@link dev_guide.services.registering_services Registering Angular Services}. The unit test example uses Jasmine spy (mock) instead of a real browser alert. -
       var mock, notify;
       
      -
       beforeEach(function() {
       mock = {alert: jasmine.createSpy()};
       notify = angular.service('notify')(mock);
       });
       
      -
       it('should not alert first two notifications', function() {
       notify('one');
       notify('two');
       expect(mock.alert).not.toHaveBeenCalled();
       });
       
      -
       it('should alert all after third notification', function() {
       notify('one');
       notify('two');
      @@ -33,7 +28,6 @@ notify('three');
       expect(mock.alert).toHaveBeenCalledWith("one\ntwo\nthree");
       });
       
      -
       it('should clear messages after alert', function() {
       notify('one');
       notify('two');
      @@ -47,24 +41,16 @@ expect(mock.alert.mostRecentCall.args).toEqual(["more\ntwo\nthird"]);
       
      - - ## Related Topics - * {@link dev_guide.services.understanding_services Understanding Angular Services} * {@link dev_guide.services.creating_services Creating Angular Services} * {@link dev_guide.services.registering_services Registering Angular Services} * {@link dev_guide.services.managing_dependencies Managing Service Dependencies} * {@link dev_guide.services.injecting_controllers Injecting Services Into Conrollers} - ## Related API - * {@link api/angular.service Angular Service API} - - - diff --git a/docs/content/guide/dev_guide.services.understanding_services.ngdoc b/docs/content/guide/dev_guide.services.understanding_services.ngdoc index f7afc981..05fafcf6 100644 --- a/docs/content/guide/dev_guide.services.understanding_services.ngdoc +++ b/docs/content/guide/dev_guide.services.understanding_services.ngdoc @@ -3,46 +3,36 @@ @name Developer Guide: Angular Services: Understanding Angular Services @description - Angular services are singletons that carry out specific tasks common to web apps, such as the {@link api/angular.service.$xhr $xhr service} that provides low level access to the browser's `XMLHttpRequest` object. - To use an angular service, you identify it as a dependency for the dependent (a controller, or another service) that depends on the service. Angular's dependency injection subsystem takes care of the rest. The angular injector subsystem is in charge of service instantiation, resolution of dependencies, and provision of dependencies to factory functions as requested. - Angular injects dependencies using "constructor" injection (the service is passed in via a factory function). Because JavaScript is a dynamically typed language, angular's dependency injection subsystem cannot use static types to identify service dependencies. For this reason a dependent must explicitly define its dependencies by using the `$inject` property. For example: - myController.$inject = ['$location']; - The angular web framework provides a set of services for common operations. Like other core angular variables and identifiers, the built-in services always start with `$` (such as `$xhr` mentioned above). You can also create your own custom services. - - ## Related Topics - * {@link dev_guide.di About Angular Dependency Injection} * {@link dev_guide.services.creating_services Creating Angular Services} * {@link dev_guide.services.registering_services Registering Angular Services} * {@link dev_guide.services.managing_dependencies Managing Service Dependencies} * {@link dev_guide.services.testing_services Testing Angular Services} - ## Related API - * {@link api/angular.service Angular Service API} * {@link api/angular.injector Injector API} diff --git a/docs/content/guide/dev_guide.templates.css.ngdoc b/docs/content/guide/dev_guide.templates.css.ngdoc index 90021b98..e1e54814 100644 --- a/docs/content/guide/dev_guide.templates.css.ngdoc +++ b/docs/content/guide/dev_guide.templates.css.ngdoc @@ -4,66 +4,48 @@ @description - - Angular includes built-in CSS classes, which in turn have predefined CSS styles. - # Built-in CSS classes - * `ng-exception` - **Usage:** angular applies this class to a DOM element if that element contains an Expression that threw an exception when evaluated. - **Styling:** The built-in styling of the ng-exception class displays an error message surrounded by a solid red border, for example: -
      Error message
      - You can try to evaluate malformed expressions in {@link dev_guide.expressions expressions} 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 api/angular.widget.@ng:validate ng:validate example}. - ## Overriding Styles for Angular CSS Classes - To override the styles for angular's built-in CSS 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: -
       
       
      - - ## Related Topics - * {@link dev_guide.templates Angular Templates} * {@link dev_guide.templates.css Working With CSS in Angular} * {@link dev_guide.templates.formatters Angular Formatters} diff --git a/docs/content/guide/dev_guide.templates.databinding.ngdoc b/docs/content/guide/dev_guide.templates.databinding.ngdoc index 1459095e..3bd0ea8f 100644 --- a/docs/content/guide/dev_guide.templates.databinding.ngdoc +++ b/docs/content/guide/dev_guide.templates.databinding.ngdoc @@ -3,16 +3,13 @@ @name Developer Guide: Templates: Data Binding in Angular @description - Data-binding in angular web apps is the automatic syncing of data between the model and view components. The way that angular implements data-binding lets you treat the model as the single-source-of-truth in your application. The view is a projection of the model at all times. When the model changes, the view reflects the change, and vice versa. - ## Data Binding in Classical Template Systems - Most templating systems bind data in only one direction: they merge template and model components together into a view, as illustrated in the diagram. After the merge occurs, changes to the model @@ -20,10 +17,8 @@ or related sections of the view are NOT automatically reflected in the view. Wor 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. - ## Data Binding in Angular Templates - The way angular templates works is different, as illustrated in the diagram. They are different because first the template (which is the uncompiled HTML along with any additional markup or @@ -33,16 +28,12 @@ the model are propagated to the view. This makes the model always the single-sou 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. - - ## Related Topics - * {@link dev_guide.scopes Angular Scopes} * {@link dev_guide.templates Angular Templates} diff --git a/docs/content/guide/dev_guide.templates.filters.creating_filters.ngdoc b/docs/content/guide/dev_guide.templates.filters.creating_filters.ngdoc index ca7fa2f2..ebb7d923 100644 --- a/docs/content/guide/dev_guide.templates.filters.creating_filters.ngdoc +++ b/docs/content/guide/dev_guide.templates.filters.creating_filters.ngdoc @@ -3,25 +3,20 @@ @name Developer Guide: Templates: Filters: Creating Angular Filters @description - Writing your own filter is very easy: just define a JavaScript function on the `angular.filter` object. 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. The `$element` variable allows the filter to manipulate the DOM. - The following sample filter reverses a text string. In addition, it conditionally makes the text upper-case and assigns color. - -
      No filter: {{text}}
      Reverse: {{text|reverse}}
      @@ -59,16 +53,11 @@ expect(binding('text|reverse')).toEqual('CBA');
      - - ## Related Topics - * {@link dev_guide.templates.filters Understanding Angular Filters} * {@link dev_guide.compiler Angular HTML Compiler} - ## Related API - * {@link api/angular.filter Angular Filter API} diff --git a/docs/content/guide/dev_guide.templates.filters.ngdoc b/docs/content/guide/dev_guide.templates.filters.ngdoc index fc0f8f84..e233678b 100644 --- a/docs/content/guide/dev_guide.templates.filters.ngdoc +++ b/docs/content/guide/dev_guide.templates.filters.ngdoc @@ -3,36 +3,26 @@ @name Developer Guide: Templates: Understanding Angular Filters @description - Angular filters format data for display to the user. In addition to formatting data, filters can also modify the DOM. This allows filters to handle tasks such as conditionally applying CSS styles to filtered output. - For example, you might have a data object that needs to be formatted according to the locale before displaying it to the user. You can pass expressions through a chain of filters like this: - name | uppercase - The expression evaluator simply passes the value of name to `angular.filter.uppercase()`. - In addition to formatting data, filters can also modify the DOM. This allows filters to handle tasks such as conditionally applying CSS styles to filtered output. - - ## Related Topics - * {@link dev_guide.templates.filters.using_filters Using Angular Filters} * {@link dev_guide.templates.filters.creating_filters Creating Angular Filters} - ## Related API - * {@link api/angular.filter Angular Filter API} diff --git a/docs/content/guide/dev_guide.templates.filters.using_filters.ngdoc b/docs/content/guide/dev_guide.templates.filters.using_filters.ngdoc index 05da5e7d..41f32ac0 100644 --- a/docs/content/guide/dev_guide.templates.filters.using_filters.ngdoc +++ b/docs/content/guide/dev_guide.templates.filters.using_filters.ngdoc @@ -3,35 +3,26 @@ @name Developer Guide: Templates: Filters: Using Angular Filters @description - Filters can be part of any {@link api/angular.scope} evaluation but are typically used to format expressions in bindings in your templates: - {{ expression | filter }} - Filters typically transform the data to a new data type, formatting the data in the process. Filters can also be chained, and can take optional arguments. - You can chain filters using this syntax: - {{ expression | filter1 | filter2 }} - You can also pass colon-delimited arguments to filters, for example, to display the number 123 with 2 decimal points: - 123 | number:2 - Here are some examples that show values before and after applying different filters to an expression in a binding: - * No filter: `{{1234.5678}}` => `1234.5678` * Number filter: `{{1234.5678|number}}` => `1,234.57`. Notice the "," and rounding to two significant digits. @@ -40,16 +31,11 @@ arguments, separated by colons in a binding. For example, the "number" filter ta argument that specifies how many digits to display to the right of the decimal point. - - ## Related Topics - * {@link dev_guide.templates.filters Understanding Angular Filters} * {@link dev_guide.templates.filters.creating_filters Creating Angular Filters} - ## Related API - * {@link api/angular.filter Angular Filter API} diff --git a/docs/content/guide/dev_guide.templates.formatters.creating_formatters.ngdoc b/docs/content/guide/dev_guide.templates.formatters.creating_formatters.ngdoc index 08504bbc..2ecd8f19 100644 --- a/docs/content/guide/dev_guide.templates.formatters.creating_formatters.ngdoc +++ b/docs/content/guide/dev_guide.templates.formatters.creating_formatters.ngdoc @@ -3,19 +3,16 @@ @name Developer Guide: Templates: Angular Formatters: Creating Angular Formatters @description - To create your own formatter, you can simply register a pair of JavaScript functions with `angular.formatter`. One of your functions is used to parse text from the input widget into the data storage format; the other function is used to format stored data into user-readable text. - The following example demonstrates a "reverse" formatter. Data is stored in uppercase and in reverse, but it is displayed in lower case and non-reversed. When a user edits the data model via the input widget, the input is automatically parsed into the internal data storage format, and when the data changes in the model, it is automatically formatted to the user-readable form for display in the view. -
       function reverse(text) {
       var reversed = [];
      @@ -25,7 +22,6 @@ reversed.unshift(text.charAt(i));
       return reversed.join('');
       }
       
      -
       angular.formatter('reverse', {
       parse: function(value){
       return reverse(value||'').toUpperCase();
      @@ -36,7 +32,6 @@ return reverse(value||'').toLowerCase();
       });
       
      - - - diff --git a/docs/content/guide/dev_guide.templates.formatters.ngdoc b/docs/content/guide/dev_guide.templates.formatters.ngdoc index 4dd8f22b..82a14fb4 100644 --- a/docs/content/guide/dev_guide.templates.formatters.ngdoc +++ b/docs/content/guide/dev_guide.templates.formatters.ngdoc @@ -3,24 +3,18 @@ @name Developer Guide: Templates: Angular Formatters @description - In angular, formatters are responsible for translating user-readable text entered in an {@link api/angular.widget.HTML input widget} to a JavaScript object in the data model that the application can manipulate. - You can use formatters in a template, and also in JavaScript. Angular provides built-in formatters, and of course you can create your own formatters. - ## Related Topics - * {@link dev_guide.templates.formatters.using_formatters Using Angular Formatters} * {@link dev_guide.templates.formatters.creating_formatters Creating Angular Formatters} - ## Related API - * {@link api/angular.formatter Angular Formatter API} diff --git a/docs/content/guide/dev_guide.templates.formatters.using_formatters.ngdoc b/docs/content/guide/dev_guide.templates.formatters.using_formatters.ngdoc index 8e160317..bf983cd5 100644 --- a/docs/content/guide/dev_guide.templates.formatters.using_formatters.ngdoc +++ b/docs/content/guide/dev_guide.templates.formatters.using_formatters.ngdoc @@ -3,10 +3,7 @@ @name Developer Guide: Templates: Angular Formatters: Using Angular Formatters @description - The following snippet shows how to use a formatter in a template. The formatter below is `ng:format="reverse"`, added as an attribute to an `` tag. -
      -
      diff --git a/docs/content/guide/dev_guide.templates.ngdoc b/docs/content/guide/dev_guide.templates.ngdoc
      index 59fed0fc..ca0ca99a 100644
      --- a/docs/content/guide/dev_guide.templates.ngdoc
      +++ b/docs/content/guide/dev_guide.templates.ngdoc
      @@ -3,17 +3,14 @@
       @name Developer Guide: Understanding Angular Templates
       @description
       
      -
       An angular template is the declarative specification that, along with information from the model
       and controller, becomes the rendered view that a user sees in the browser. It is the static DOM,
       containing HTML, CSS, and angular-specific elements and angular-specific element attributes.  The
       angular elements and attributes direct angular to add behavior and transform the template DOM into
       the dynamic view DOM.
       
      -
       These are the types of angular elements and element attributes you can use in a template:
       
      -
       * {@link dev_guide.compiler.directives Directive} — An attribute that augments an existing DOM
       element.
       * {@link dev_guide.compiler.widgets Widget} — A custom DOM element. An example of a built-in widget
      @@ -25,16 +22,13 @@ curly brace notation `{{ }}` to bind expressions to elements is built-in angular
       * {@link dev_guide.templates.formatters Formatter} — Lets you format the input object into a user
       readable view.
       
      -
       Note:  In addition to declaring the elements above in templates, you can also access these elements
       in JavaScript code.
       
      -
       The following code snippet shows a simple angular template made up of standard HTML tags along with
       angular {@link dev_guide.compiler.directives directives}, {@link dev_guide.compiler.markup markup},
       and {@link dev_guide.expressions expressions}:
       
      -
       
       
        
      @@ -49,7 +43,6 @@ and {@link dev_guide.expressions expressions}:
       
       
      - In a simple single-page app, the template consists of HTML, CSS, and angular directives contained in just one HTML file (usually `index.html`). In a more complex app, you can display multiple views within one main page using "partials", which are segments of template located in separate HTML @@ -59,17 +52,12 @@ example of this technique is shown in the {@link tutorial/ angular tutorial}, in eight. - - ## Related Topics - * {@link dev_guide.templates.filters Angular Filters} * {@link dev_guide.templates.formatters Angular Formatters} * {@link dev_guide.templates.validators Angular Validators} - ## Related API - * {@link api/index API Reference} diff --git a/docs/content/guide/dev_guide.templates.validators.creating_validators.ngdoc b/docs/content/guide/dev_guide.templates.validators.creating_validators.ngdoc index 661ce744..835b0b51 100644 --- a/docs/content/guide/dev_guide.templates.validators.creating_validators.ngdoc +++ b/docs/content/guide/dev_guide.templates.validators.creating_validators.ngdoc @@ -4,17 +4,13 @@ @description - - To create a custom validator, you simply add your validator code as a method onto the `angular.validator` object and provide input(s) for the validator function. Each input provided is treated as an argument to the validator function. Any additional inputs should be separated by commas. - The following bit of pseudo-code shows how to set up a custom validator: -
       angular.validator('your_validator', function(input [,additional params]) {
               [your validation code];
      @@ -26,22 +22,17 @@ angular.validator('your_validator', function(input [,additional params]) {
       }
       
      - Note that this validator returns "true" when the user's input is incorrect, as in "Yes, it's true, there was a problem with that input". If you prefer to provide more information when a validator detects a problem with input, you can specify an error message in the validator that angular will display when the user hovers over the input widget. - To specify an error message, replace "`return true;`" with an error string, for example: - return "Must be a value between 1 and 5!"; - Following is a sample UPS Tracking Number validator: -
      - (For details on what happens when `angular` processes an HTML page, see {@link guide/dev_guide.bootstrap Bootstrap}.) - Finally, this line in the `` of the page is the template that describes how to display our greeting in the UI: -
           Hello {{'World'}}!
       
      - Note the use of the double curly brace markup (`{{ }}`) to bind the expression to the greeting text. Here the expression is the string literal 'World'. - Next let's look at a more interesting example, that uses `angular` to bind a dynamic expression to our greeting text. - # Hello World! - This example demonstrates `angular`'s two-way data binding: - 1. Edit the HTML file you created in the "Hello World!" example above. 2. Replace the contents of `` with the code from the __Source__ box below. 3. Refresh your browswer window. - Your name: @@ -93,24 +73,18 @@ This example demonstrates `angular`'s two-way data binding: - After the refresh, the page should look something like this: - - These are some of the important points to note from this example: - * The text input {@link api/angular.widget widget} called `yourname` is bound to a model variable called `yourname`. * The double curly braces notation binds the `yourname` model to the greeting text. - * You did not need to explicitly register an event listener or define an event handler for events! - Now try typing your name into the input box, and notice the immediate change to the displayed greeting. This demonstrates the concept of `angular`'s {@link guide/dev_guide.templates.databinding bi-directional data binding}. Any changes to the input @@ -119,70 +93,50 @@ 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. - - ## Data - The Model is referenced from properties on {@link guide/dev_guide.scopes angular scope objects}. The data in your model could be Javascript objects, arrays, or primitives, it doesn't matter. What matters is that these are all referenced by the scope object. - Angular employs scopes to keep your data model and your UI in sync. Whenever something occurs to change the state of the model, angular immediately reflects that change in the UI, and vice versa. - The following illustration shows the parts of an angular application and how they work together: - - In addition, angular comes with a set of Services, which have the following properties: - * The services provided are very useful for building web applications. * You can extend and add application-specific behavior to services. * Services include Dependency-Injection, XHR, caching, URL routing, and browser abstraction. - - # Where To Go Next - * For explanations and examples of the angular concepts presented on this page, see the {@link guide/index Developer Guide}. - * 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}. diff --git a/docs/content/tutorial/index.ngdoc b/docs/content/tutorial/index.ngdoc index 68793134..605bccd3 100644 --- a/docs/content/tutorial/index.ngdoc +++ b/docs/content/tutorial/index.ngdoc @@ -2,20 +2,16 @@ @name Tutorial @description - A great way to get introduced to angular is to work through this tutorial, which walks you through the construction of an angular web app. The app you will build is a catalog that displays a list of Android devices, lets you filter the list to see only devices that interest you, and then view details for any device. - - As you work through this tutorial, you will learn how angular makes browsers smarter — without the use of extensions or plugins. - * You will see examples of how to use client-side data binding and dependency injection to build dynamic views of data that change immediately in response to user actions. * You will see how angular creates listeners on your data without the need for DOM manipulation. @@ -23,13 +19,10 @@ dynamic views of data that change immediately in response to user actions. * You will learn how to use angular services to make common web tasks, such as getting data into your app, easier. - And all of this works in any browser without modifications! - When you finish the tutorial you will be able to: - * Create a dynamic application that works in any browser * Define the differences between angular and common JavaScript frameworks * Understand how data binding works in angular @@ -37,12 +30,10 @@ When you finish the tutorial you will be able to: * Create and run tests * Identify resources for learning more about angular - The tutorial is will guide you through the process of building a simple application, including writing and running unit and end-to-end tests, and will allow you to experiment with angular and the application through experiments suggested at the end of each step. - You can go through the whole tutorial in a couple of hours or you may want to spend a pleasant day really digging into it. If you're looking for a shorter introduction to angular, check out the {@link misc/started Getting Started} document. @@ -53,23 +44,14 @@ really digging into it. If you're looking for a shorter introduction to angular, - - - - - - - # Working with the code - There are two ways that you can you follow this tutorial and hack on the code, both available on Mac/Linux or Windows environment. The first work flow uses Git versioning system for source code management, the second work flow doesn't depend on any source control system and instead uses scripts to copy snapshots of project files into your workspace (`sandbox`) directory. Choose the one you prefer: -
        @@ -95,7 +77,6 @@ server.

      -
      1. Verify that you have Java installed and that the @@ -123,7 +104,6 @@ href="http://node-js.prcn.co.cc/">pre-compiled binaries, unzip them and add

      -
      1. Verify that you have Java installed by running the @@ -144,7 +124,6 @@ server.

      -
      1. Verify that you have Java installed and that the @@ -167,9 +146,6 @@ href="http://node-js.prcn.co.cc/">pre-compiled binaries, unzip them and add - For either work flow you'll also need a web browser and your favorite text editor. - Let's get going with {@link step_00 step 0}. - diff --git a/docs/content/tutorial/step_00.ngdoc b/docs/content/tutorial/step_00.ngdoc index 09b06109..a8243d47 100644 --- a/docs/content/tutorial/step_00.ngdoc +++ b/docs/content/tutorial/step_00.ngdoc @@ -2,19 +2,14 @@ @name Tutorial: 0 - angular-seed @description -

          - - You are now ready to build the phonecat application. In this step, you will become familiar with the most important source code files, learn how to start the development servers bundled with angular-seed, and run the application in the browser. - -
            @@ -25,7 +20,6 @@ angular-seed, and run the application in the browser. the number of the step you are on. This will cause any changes you made within your working directory to be lost.

            -
          1. To see the app running in a browser, do one of the following:
            • For node.js users: @@ -50,8 +44,6 @@ directory.
            • - -
              1. Open msysGit bash and run this command (in angular-phonecat directory):

                @@ -84,8 +76,6 @@ directory.
              2. - -
                1. In angular-phonecat directory, run this command:

                  @@ -118,8 +108,6 @@ href="http://localhost:8000/app/index.html">http://localhost:8000/app/index.html - -
                  1. Open windows command line and run this command (in angular-phonecat directory):

                    @@ -153,15 +141,11 @@ href="http://localhost:8000/app/index.html">http://localhost:8000/app/index.html - - You can now see the page in your browser. It's not very exciting, but that's OK. - The static HTML page that displays "Nothing here yet!" was constructed with the HTML code shown below. The code contains some key angular elements that we will need going forward. - __`app/index.html`:__
                     
                    @@ -173,10 +157,8 @@ __`app/index.html`:__
                     
                     
                     
                    -
                       Nothing here yet!
                     
                    -
                       
                     
                     
                    @@ -184,73 +166,51 @@ __`app/index.html`:__
                     
                     
                     
                    -
                    -
                    -
                     ## What is the code doing?
                     
                    -
                     * xmlns declaration
                     
                    -
                               
                     
                    -
                       This `xmlns` declaration for the `ng` namespace must be specified in all angular applications in
                     order to make angular work with XHTML and IE versions older than 9 (regardless of whether you are
                     using XHTML or HTML).
                     
                    -
                     * angular script tag
                     
                    -
                               
                       
                     
                     
                     
                    - We replaced the hard-coded phone list with the {@link api/angular.widget.@ng:repeat ng:repeat widget} and two {@link guide/dev_guide.expressions angular expressions} enclosed in curly braces: `{{phone.name}}` and `{{phone.snippet}}`: - * The `ng:repeat="phone in phones"` statement in the `
                  2. ` tag is an angular repeater. It tells angular to create a `
                  3. ` element for each phone in the phones list, using the first `
                  4. ` tag as the template. - - * The curly braces around `phone.name` and `phone.snippet` are an example of {@link guide/dev_guide.compiler.markup angular markup}. The curly markup is shorthand for the angular directive {@link api/angular.directive.ng:bind ng:bind}. The `ng:bind` directives indicate to @@ -83,15 +64,11 @@ of the model through the HTML template. This means that whenever the model chang refreshes the appropriate binding points, which updates the view. - - ## Model and Controller - The data __model__ (a simple array of phones in object literal notation) is instantiated within the __controller__ function (`PhoneListCtrl`): - __`app/js/controllers.js`:__
                     function PhoneListCtrl() {
                    @@ -107,22 +84,16 @@ function PhoneListCtrl() {
                     
                     
                     
                    -
                    -
                    -
                    -
                     Although the controller is not yet doing very much controlling, it is playing a crucial role. By
                     providing context for our data model, the controller allows us to 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
                     api/angular.directive.ng:controller ng:controller} directive in the `` tag (`PhoneListCtrl`).
                     * We instantiated our data within the scope of our controller function, and our template binding
                     points are located within the block bounded by the `` tag.
                     
                    -
                     Angular scopes are a crucial concept in angular; you can think of scopes as the glue that makes the
                     template, model and controller all work together. Angular uses scopes, along with the information
                     contained in the template, data model, and controller, to keep the model and view separated but in
                    @@ -131,23 +102,17 @@ reflected in the model. To learn more about angular scopes, see the {@link api/a
                     angular scope documentation}.
                     
                     
                    -
                    -
                     ## Tests
                     
                    -
                     The "Angular way" makes it easy for us to test as we develop; the unit test for your newly created
                     controller looks as follows:
                     
                    -
                     __`test/unit/controllersSpec.js`:__
                     
                     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);
                    @@ -156,105 +121,76 @@ describe('PhoneCat controllers', function() {
                     });
                     
                    - 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. The test verifies that we have 3 records in the phones array. - Angular developers prefer the syntax of Jasmine's Behavior-driven Development (BDD) framework when writing tests. Although Jasmine is not required by angular, we used 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}. - The angular-seed project is pre-configured to run all unit tests using {@link http://code.google.com/p/js-test-driver/ JsTestDriver}. To run the test, do the following: - 1. In a _separate_ terminal window or tab, go to the `angular-phonecat` directory and run `./scripts/test-server.sh` to start the test web server. - 2. Open a new browser tab or window and navigate to {@link http://localhost:9876}. - 3. Choose "Capture this browser in strict mode". - At this point, you can leave this tab open and forget about it. JsTestDriver will use it to execute the tests and report the results in the terminal. - 4. Execute the test by running `./scripts/test.sh` - You should see the following or similar output: - Chrome: Runner reset. . Total 1 tests (Passed: 1; Fails: 0; Errors: 0) (2.00 ms) Chrome 11.0.696.57 Mac OS: Run 1 tests (Passed: 1; Fails: 0; Errors 0) (2.00 ms) - Yay! The test passed! Or not... - Note: If you see errors after you run the test, close the browser tab and go back to the terminal and kill the script, then repeat the procedure above. - # Experiments - * Add another binding to `index.html`. For example: -

                    Total number of phones: {{phones.length}}

                    - * Create a new model property in the controller and bind to it from the template. For example: - this.hello = "Hello, World!" - Refresh your browser to make sure it says, "Hello, World!" - * Create a repeater that constructs a simple table: -
                    row number
                    {{i}}
                    - Now, make the list 1-based by incrementing `i` by one in the binding: -
                    row number
                    {{i+1}}
                    - * Make the unit test fail by changing the `toBe(3)` statement to `toBe(4)`, and rerun the `./scripts/test.sh` script. - - # Summary - You now have a dynamic app that features separate model, view, and controller components, and you're testing as you go. Now, let's go to step 3 to learn how to add full text search to the app. - -
                      - diff --git a/docs/content/tutorial/step_03.ngdoc b/docs/content/tutorial/step_03.ngdoc index c4654998..1ebde553 100644 --- a/docs/content/tutorial/step_03.ngdoc +++ b/docs/content/tutorial/step_03.ngdoc @@ -2,53 +2,38 @@ @name Tutorial: 3 - Filtering Repeaters @description -
                        - - We did a lot of work in laying a foundation for the app in the last step, so now we'll do something simple, and add full text search (yes, it will be simple!). 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. - - - - The app now has a search box. The phone list on the page changes depending on what a user types into the search box. - The most important changes are listed below. You can see the full diff on {@link https://github.com/angular/angular-phonecat/compare/step-2...step-3 GitHub}: - - ## Controller - We made no changes to the controller. - - ## Template - __`app/index.html`:__
                         ...
                            Fulltext Search: 
                         
                        -
                           
                        • {{phone.name}} @@ -58,70 +43,54 @@ __`app/index.html`:__ ...
                        - We added a standard HTML `` tag and use angular's {@link api/angular.Array.filter $filter} function to process the input for the `ng:repeater`. - This lets a user enter search criteria and immediately see the effects of their search on the phone list. This new code demonstrates the following: - * 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 code, the data that a user types into the input box (named __`query`__) is immediately available as a filter input in the list repeater (`phone in phones.$filter(`__`query`__`)`). When changes to the data model cause the repeater's input to change, the repeater efficiently updates the DOM to reflect the current state of the model. - - * Use of `$filter`. The {@link api/angular.Array.filter $filter} method, uses the `query` value, to create a new array that contains only those records that match the `query`. - `ng:repeat` automatically updates the view in response to the changing number of phones returned by the `$filter`. The process is completely transparent to the developer. - ## Test - In step 2, we learned how to write and run unit tests. Unit tests are perfect for testing controllers and other components of our application written in JavaScript, but they can't easily test DOM manipulation or the wiring of our application. For these, an end-to-end test is a much better choice. - The search feature was fully implemented via templates and data-binding, so we'll write our first end-to-end test, to verify that the feature works. - __`test/e2e/scenarios.js`:__
                         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);
                             });
                        @@ -129,64 +98,48 @@ describe('PhoneCat App', function() {
                         });
                         
                        - Even though the syntax of this test looks very much like our controller unit test written with Jasmine, the end-to-end test uses APIs of {@link https://docs.google.com/document/d/11L8htLKrh6c92foV71ytYpiKkeKpM4_a5-9c3HywfIc/edit?hl=en&pli=1# angular's end-to-end test runner}. - To run the end-to-end test, open the following in a new browser tab: - * node.js users: {@link http://localhost:8000/test/e2e/runner.html} * users with other http servers: `http://localhost:[port-number]/[context-path]/test/e2e/runner.html` * casual reader: {@link http://angular.github.com/angular-phonecat/step-3/test/e2e/runner.html} - This test verifies that the search box and the repeater are correctly wired together. Notice how easy it is to write end-to-end tests in angular. Although this example is for a simple test, it really is that easy to set up any functional, readable, end-to-end test. - # Experiments - * Display the current value of the `query` model by adding a `{{query}}` binding into the `index.html` template, and see how it changes when you type in the input box. - * Let's see how we can get the current value of the `query` model to appear in the HTML page title. - You might think you could just add the {{query}} to the title tag element as follows: - Google Phone Gallery: {{query}} - However, when you reload the page, you won't see the expected result. This is because the "query" model lives in the scope defined by the body element: - - If you want to bind to the query model from the `` element, you must __move__ the `ng:controller` declaration to the HTML element because it is the common parent of both the body and title elements: - <html ng:controller="PhoneListCtrl"> - Be sure to *remove* the `ng:controller` declaration from the body element. - * Add the following end-to-end test into the `describe` block within `test/e2e/scenarios.js`: - <pre> it('should display the current filter value within an element with id "status"', function() { @@ -201,30 +154,21 @@ and title elements: }); </pre> - Refresh the browser tab with end-to-end test runner to see the test fail. Now add a `div` or `p` element with `id` `"status"` and content with the `query` binding into the `index.html` template to make the test pass. - * Add a `pause()` statement into an end-to-end test and rerun it. You'll see the runner pausing, giving you the opportunity to explore the state of your application displayed in the browser. The app is live! Change the search query to prove it. This is great for troubleshooting end-to-end tests. - - # Summary - With full text search under our belt and a test to verify it, let's go to step 4 to learn how to add sorting capability to the phone app. - - <ul doc:tutorial-nav="3"></ul> - - diff --git a/docs/content/tutorial/step_04.ngdoc b/docs/content/tutorial/step_04.ngdoc index 39bb3d51..48084980 100644 --- a/docs/content/tutorial/step_04.ngdoc +++ b/docs/content/tutorial/step_04.ngdoc @@ -2,38 +2,27 @@ @name Tutorial: 4 - Two-way Data Binding @description - <ul doc:tutorial-nav="4"></ul> - - In this step, you will add a feature to let your users control the order of the items in the phone list. The dynamic ordering is implemented by creating a new model property, wiring it together with the repeater, and letting the data binding magic do the rest of the work. - - <doc:tutorial-instructions step="4"></doc:tutorial-instructions> - - You should see that in addition to the search box, the app displays a drop down menu that allows users to control the order in which the phones are listed. - The most important changes are listed below. You can see the full diff on {@link https://github.com/angular/angular-phonecat/compare/step-3...step-4 GitHub}: - - ## Template - __`app/index.html`:__ <pre> ... @@ -50,7 +39,6 @@ __`app/index.html`:__ </li> </ul> - <ul class="phones"> <li ng:repeat="phone in phones.$filter(query).$orderBy(orderProp)"> {{phone.name}} @@ -60,26 +48,20 @@ __`app/index.html`:__ ... </pre> - In the `index.html` template we made the following changes: - * First, we added a `<select>` html element named `orderProp`, so that our users can pick from the two provided sorting options. - <img src="img/tutorial/tutorial_04-06_final.png"> - * We then chained the `$filter` method with {@link api/angular.Array.orderBy `$orderBy`} method to further process the input into the repeater. `$orderBy` is a utility method similar to {@link api/angular.Array.filter `$filter`}, but instead of filtering an array, it reorders it. - Angular creates a two way data-binding between the select element and the `orderProp` model. `orderProp` is then used as the input for the `$orderBy` method. - As we discussed in the section about data-binding and the repeater in step 3, whenever the model changes (for example because a user changes the order with the select drop down menu), angular's data-binding will cause the view to automatically update. No bloated DOM manipulation code is @@ -87,17 +69,12 @@ necessary! - - - ## Controller - __`app/js/controller.js`:__ <pre> /* App Controllers */ - function PhoneListCtrl() { this.phones = [{"name": "Nexus S", "snippet": "Fast just got faster with Nexus S.", @@ -109,21 +86,17 @@ function PhoneListCtrl() { "snippet": "The Next, Next Generation tablet.", "age": 2}]; - this.orderProp = 'age'; } </pre> - * We modified the `phones` model - the array of phones - and added an `age` property to each phone record. This property is used to order phones by age. - * We added a line to the controller that sets the default value of `orderProp` to `age`. If we had not set the default value here, angular would have used the value of the first `<option>` element (`'name'`) when it initialized the data model. - This is a good time to talk about two-way data-binding. Notice that when the app is loaded in the browser, "Newest" is selected in the drop down menu. This is because we set `orderProp` to `'age'` in the controller. So the binding works in the direction from our model to the UI. Now if you @@ -133,39 +106,28 @@ to the model. - - - ## Test - The changes we made should be verified with both a unit test and an end-to-end test. Let's look at the unit test first. - __`test/unit/controllerSpec.js`:__ <pre> 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'); }); @@ -174,49 +136,37 @@ describe('PhoneCat controllers', function() { </pre> - - The unit test now verifies that the default ordering property is set. - We used Jasmine's API to extract the controller construction into a `beforeEach` block, which is shared by all tests in the parent `describe` block. - To run the unit tests, once again execute the `./scripts/test.sh` script and you should see the following output. - Chrome: Runner reset. .. Total 2 tests (Passed: 2; Fails: 0; Errors: 0) (3.00 ms) Chrome 11.0.696.57 Mac OS: Run 2 tests (Passed: 2; Fails: 0; Errors 0) (3.00 ms) - - Let's turn our attention to the end-to-end test. - __`test/e2e/scenarios.js`:__ <pre> ... it('should be possible to control phone order via the drop down select box', function() { - // narrow the dataset to make the test assertions shorter input('query').enter('tablet'); - 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"]); @@ -224,36 +174,26 @@ __`test/e2e/scenarios.js`:__ ... </pre> - The end-to-end test verifies that the ordering mechanism of the select box is working correctly. - You can now refresh the browser tab with the end-to-end test runner to see the tests run, or you can see them running on {@link http://angular.github.com/angular-phonecat/step-4/test/e2e/runner.html angular's server}. - # Experiments - * In the `PhoneListCtrl` controller, remove the statement that sets the `orderProp` value and you'll see that the ordering as well as the current selection in the dropdown menu will default to "Alphabetical". - * Add an `{{orderProp}}` binding into the `index.html` template to display its current value as text. - # Summary - Now that you have added list sorting and tested the app, go to step 5 to learn about angular services and how angular uses dependency injection. - - <ul doc:tutorial-nav="4"></ul> - diff --git a/docs/content/tutorial/step_05.ngdoc b/docs/content/tutorial/step_05.ngdoc index 68cc726a..1aac51f7 100644 --- a/docs/content/tutorial/step_05.ngdoc +++ b/docs/content/tutorial/step_05.ngdoc @@ -2,40 +2,29 @@ @name Tutorial: 5 - XHRs & Dependency Injection @description - <ul doc:tutorial-nav="5"></ul> - - Enough of building an app with three phones in a hard-coded dataset! Let's fetch a larger dataset from our server using one of angular's built-in {@link api/angular.service services} called {@link api/angular.service.$xhr $xhr}. We will use angular's {@link guide/dev_guide.di dependency injection (DI)} to provide the service to the `PhoneListCtrl` controller. - - <doc:tutorial-instructions step="5"></doc:tutorial-instructions> - - You should now see a list of 20 phones. - The most important changes are listed below. You can see the full diff on {@link https://github.com/angular/angular-phonecat/compare/step-4...step-5 GitHub}: - ## Data - The `app/phones/phone.json` file in your project is a dataset that contains a larger list of phones stored in the JSON format. - Following is a sample of the file: <pre> [ @@ -51,125 +40,96 @@ Following is a sample of the file: </pre> - - ## Controller - We'll use angular's {@link api/angular.service.$xhr $xhr} service in our controller to make an HTTP request to your web server to fetch the data in the `app/phones/phones.json` file. `$xhr` is just one of several built-in {@link api/angular.service angular services} that handle common operations in web apps. Angular injects these services for you where you need them. - Services are managed by angular's {@link guide/dev_guide.di DI subsystem}. Dependency injection helps to make your web apps both well-structured (e.g., separate components for presentation, data, and control) and loosely coupled (dependencies between components are not resolved by the components themselves, but by the DI subsystem). - __`app/js/controllers.js:`__ <pre> function PhoneListCtrl($xhr) { var self = this; - $xhr('GET', 'phones/phones.json', function(code, response) { self.phones = response; }); - self.orderProp = 'age'; } - //PhoneListCtrl.$inject = ['$xhr']; </pre> - `$xhr` makes an HTTP GET request to our web server, asking for `phone/phones.json` (the url is relative to our `index.html` file). The server responds by providing the data in the json file. (The response might just as well have been dynamically generated by a backend server. To the browser and our app they both look the same. For the sake of simplicity we used a json file in this tutorial.) - The `$xhr` service takes a callback as the last argument. This callback is used to process the response. We assign the response to the scope controlled by the controller, as a model called `phones`. Notice that angular detected the json response and parsed it for us! - To use a service in angular, you simply declare the names of the services you need as arguments to the controller's constructor function, as follows: - function PhoneListCtrl($xhr) {...} - Angular's dependency injector provides services to your controller when the controller is being constructed. The dependency injector also takes care of creating any transitive dependencies the service may have (services often depend upon other services). - <img src="img/tutorial/xhr_service_final.png"> - - ### '$' Prefix Naming Convention - You can create your own services, and in fact we will do exactly that in step 11. As a naming convention, angular's built-in services, Scope methods and a few other angular APIs have a '$' prefix in front of the name. Don't use a '$' prefix when naming your services and models, in order to avoid any possible naming collisions. - ### A Note on Minification - Since angular infers the controller's dependencies from the names of arguments to the controller's constructor function, if you were to {@link http://en.wikipedia.org/wiki/Minification_(programming) minify} the JavaScript code for `PhoneListCtrl` controller, all of its function arguments would be minified as well, and the dependency injector would not being able to identify services correctly. - To overcome issues caused by minification, just assign an array with service identifier strings into the `$inject` property of the controller function, just like the last line in the snippet (commented out) suggests: - PhoneListCtrl.$inject = ['$xhr']; - - ## Test - __`test/unit/controllersSpec.js`:__ - Because we started using dependency injection and our controller has dependencies, constructing the controller in our tests is a bit more complicated. We could use the `new` operator and provide the constructor with some kind of fake `$xhr` implementation. However, the recommended (and easier) way is to create a controller in the test environment in the same way that angular does it in the production code behind the scenes, as follows: - <pre> 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'}]); @@ -178,59 +138,46 @@ describe('PhoneCat controllers', function() { }); </pre> - We created the controller in the test environment, as follows: - * We created a root scope object by calling `angular.scope()` - * We called `scope.$new(PhoneListCtrl)` to get angular to create the child scope associated with the `PhoneListCtrl` controller - Because our code now uses the `$xhr` service to fetch the phone list data in our controller, before we create the `PhoneListCtrl` child scope, we need to tell the testing harness to expect an incoming request from the controller. To do this we: - * Use the {@link api/angular.scope.$service `$service`} method to retrieve the `$browser` service, a service that angular uses to represent 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. - * Use the `$browser.xhr.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. - Now, we will make assertions to verify that the `phones` model doesn't exist on the scope, before the response is received: - <pre> 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'}]); }); </pre> - * 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. - * We make the assertions, verifying that the phone model now exists on the scope. - Finally, we verify that the default value of `orderProp` is set correctly: - <pre> it('should set the default value of orderProp model', function() { expect(ctrl.orderProp).toBe('age'); @@ -239,44 +186,31 @@ Finally, we verify that the default value of `orderProp` is set correctly: }); </pre> - To run the unit tests, execute the `./scripts/test.sh` script and you should see the following output. - Chrome: Runner reset. .. Total 2 tests (Passed: 2; Fails: 0; Errors: 0) (3.00 ms) Chrome 11.0.696.57 Mac OS: Run 2 tests (Passed: 2; Fails: 0; Errors 0) (3.00 ms) - - # Experiments - * At the bottom of `index.html`, add a `{{phones}}` binding to see the list of phones displayed in json format. - * In the `PhoneListCtrl` controller, pre-process the xhr response by limiting the number of phones to the first 5 in the list. Use the following code in the xhr callback: - self.phones = response.splice(0, 5); - - # Summary - Now that you have learned how easy it is to use angular services (thanks to angular's implementation of dependency injection), go to step 6, where you will add some thumbnail images of phones and some links. - - <ul doc:tutorial-nav="5"></ul> - diff --git a/docs/content/tutorial/step_06.ngdoc b/docs/content/tutorial/step_06.ngdoc index e7fa1660..aa628a5d 100644 --- a/docs/content/tutorial/step_06.ngdoc +++ b/docs/content/tutorial/step_06.ngdoc @@ -2,41 +2,29 @@ @name Tutorial: 6 - Templating Links & Images @description - <ul doc:tutorial-nav="6"></ul> - - In this step, you will add thumbnail images for the phones in the phone list, and links that, for now, will go nowhere. In subsequent steps you will use the links to display additional information about the phones in the catalog. - - <doc:tutorial-instructions step="6"></doc:tutorial-instructions> - - You should now see links and images of the phones in the list. - The most important changes are listed below. You can see the full diff on {@link https://github.com/angular/angular-phonecat/compare/step-5...step-6 GitHub}: - - ## Data - Note that the `phones.json` file contains unique ids and image urls for each of the phones. The urls point to the `app/img/phones/` directory. - __`app/phones/phones.json`__ (sample snippet): <pre> [ @@ -52,11 +40,8 @@ __`app/phones/phones.json`__ (sample snippet): </pre> - - ## Template - __`app/index.html`:__ <pre> ... @@ -70,13 +55,11 @@ __`app/index.html`:__ ... </pre> - To dynamically generate links that will in the future lead to phone detail pages, we used the now-familiar {@link guide/dev_guide.compiler.markup double-curly brace markup} in the `href` attribute values. In step 2, we added the `{{phone.name}}` binding as the element content. In this step the '{{phone.id}}' binding is used in the element attribute. - We also added phone images next to each record using an image tag with the {@link api/angular.directive.ng:src ng:src} directive. That directive prevents the browser from treating the angular `{{ exppression }}` markup literally, which it would have done if we had only specified @@ -84,11 +67,8 @@ an attribute binding in a regular `src` attribute (`<img src="{{phone.imageUrl}} `ng:src` prevents the browser from making an http request to an invalid location. - - ## Test - __`test/e2e/scenarios.js`__: <pre> ... @@ -100,36 +80,26 @@ __`test/e2e/scenarios.js`__: ... </pre> - We added a new end-to-end test to verify that the app is generating correct links to the phone views that we will implement in the upcoming steps. - You can now refresh the browser tab with the end-to-end test runner to see the tests run, or you can see them running on {@link http://angular.github.com/angular-phonecat/step-6/test/e2e/runner.html angular's server}. - # Experiments - * Replace the `ng:src` directive with a plain old `<src>` attribute. Using tools such as Firebug, or Chrome's Web Inspector, or inspecting the webserver access logs, confirm that the app is indeed making an extraneous request to `/app/%7B%7Bphone.imageUrl%7D%7D` (or `/app/index.html/{{phone.imageUrl}}`). - - # Summary - Now that you have added phone images and links, go to step 7 to learn about angular layout templates and how angular makes it easy to create applications that have multiple views. - - <ul doc:tutorial-nav="6"></ul> - diff --git a/docs/content/tutorial/step_07.ngdoc b/docs/content/tutorial/step_07.ngdoc index cda63463..4f6a768e 100644 --- a/docs/content/tutorial/step_07.ngdoc +++ b/docs/content/tutorial/step_07.ngdoc @@ -2,52 +2,38 @@ @name Tutorial: 7 - Routing & Multiple Views @description - <ul doc:tutorial-nav="7"></ul> - - In this step, you will learn how to create a layout template and how to build an app that has multiple views by adding routing. - - <doc:tutorial-instructions step="7"></doc:tutorial-instructions> - - Note that you are redirected to `app/index.html#/phones` and the same phone list appears in the browser. When you click on a phone link the stub of a phone detail page is displayed. - - The most important changes are listed below. You can see the full diff on {@link https://github.com/angular/angular-phonecat/compare/step-6...step-7 GitHub}: - - ## Multiple Views, Routing and Layout Template - Our app is slowly growing and becoming more complex. Before step 7, the app provided our users with a single view (the list of all phones), and all of the template code was located in the `index.html` file. The next step in building the app is to add 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. - Application routes in angular are declared via the {@link api/angular.service.$route $route} service. This service makes it easy to wire together controllers, view templates, and the current URL location in the browser. Using this feature we can implement {@link @@ -55,106 +41,83 @@ http://en.wikipedia.org/wiki/Deep_linking deep linking}, which lets us utilize t history (back and forward navigation) and bookmarks. - - ## Controllers - __`app/js/controller.js`:__ <pre> 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']; ... </pre> - We created a new controller called `PhoneCatCtrl`. We declared its dependency on the `$route` service and used this service to declare that our application consists of two different views: - * The phone list view will be shown when the URL hash fragment is `/phone`. To construct this view, angular will use the `phone-list.html` template and the `PhoneListCtrl` controller. - * The phone details view will be shown when the URL hash fragment matches '/phone/:phoneId', where `:phoneId` is a variable part of the URL. To construct the phone details view, angular will use the `phone-detail.html` template and the `PhoneDetailCtrl` controller. - We reused the `PhoneListCtrl` controller that we constructed in previous steps and we added a new, empty `PhoneDetailCtrl` controller to the `app/js/controllers.js` file for the phone details view. - The statement `$route.otherwise({redirectTo: '/phones'})` triggers a redirection to `/phones` when the browser address doesn't match either of our routes. - Thanks to the `$route.parent(this);` statement and `ng:controller="PhoneCatCtrl"` declaration in the `index.html` template, the `PhoneCatCtrl` controller has a special role in our app. It is the "root" controller and the parent controller for the other two sub-controllers (`PhoneListCtrl` and `PhoneDetailCtrl`). The sub-controllers inherit the model properties and behavior from the root controller. - Note the use of the `:phoneId` parameter in the second route declaration. The `$route` service uses the route declaration — `'/phones/:phoneId'` — as a template that is matched against the current URL. All variables defined with the `:` notation are extracted into the `$route.current.params` map. - The `params` alias created in the {@link api/angular.service.$route `$route.onChange`} callback allows us to use the `phoneId` property of this map in the `phone-details.html` template. - - ## Template - The `$route` service is usually used in conjunction with the {@link api/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. - __`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> - Note that we removed most of the code in the `index.html` template and replaced it with a single line containing the `ng:view` tag. The code that we removed was placed into the `phone-list.html` template: - __`app/partials/phone-list.html`:__ <pre> <ul class="predicates"> @@ -170,7 +133,6 @@ __`app/partials/phone-list.html`:__ </li> </ul> - <ul class="phones"> <li ng:repeat="phone in phones.$filter(query).$orderBy(orderProp)"> <a href="#/phones/{{phone.id}}">{{phone.name}}</a> @@ -180,31 +142,23 @@ __`app/partials/phone-list.html`:__ </ul> </pre> - <img src="img/tutorial/tutorial_07_final.png"> - We also added a placeholder template for the phone details view: - __`app/partials/phone-list.html`:__ <pre> TBD: detail view for {{params.phoneId}} </pre> - Note how we are using `params` model defined in the `PhoneCanCtrl` controller. - - ## Test - To automatically verify that everything is wired properly, we wrote end-to-end tests that navigate to various URLs and verify that the correct view was rendered. - <pre> ... it('should redirect index.html to index.html#/phones', function() { @@ -213,17 +167,13 @@ to various URLs and verify that the correct view was rendered. }); ... - describe('Phone detail view', function() { - beforeEach(function() { browser().navigateTo('../../app/index.html#/phones/nexus-s'); }); - - it('should display placeholder page with phoneId', function() { expect(binding('params.phoneId')).toBe('nexus-s'); }); @@ -231,40 +181,29 @@ to various URLs and verify that the correct view was rendered. </pre> - - You can now refresh the browser tab with the end-to-end test runner to see the tests run, or you can see them running on {@link http://angular.github.com/angular-phonecat/step-7/test/e2e/runner.html angular's server}. - - # Experiments - * Try to add an `{{orderProp}}` binding to `index.html`, and you'll see that nothing happens even when you are in the phone list view. This is because the `orderProp` model is visible only in the scope managed by `PhoneListCtrl`, which is associated with the `<ng:view>` element. If you add the same binding into the `phone-list.html` template, the binding will work as expected. - * In `PhoneCatCtrl`, create a new model called "`hero`" with `this.hero = 'Zoro'`. In `PhoneListCtrl` let's shadow it with `this.hero = 'Batman'`, and in `PhoneDetailCtrl` we'll use `this.hero = "Captain Proton"`. Then add the `<p>hero = {{hero}}</p>` to all three of our templates (`index.html`, `phone-list.html`, and `phone-detail.html`). Open the app and you'll see scope inheritance and model property shadowing do some wonders. - # Summary - With the routing set up and the phone list view implemented, we're ready to go to step 8 to implement the phone details view. - - <ul doc:tutorial-nav="7"></ul> - diff --git a/docs/content/tutorial/step_08.ngdoc b/docs/content/tutorial/step_08.ngdoc index a430387b..e81a8d93 100644 --- a/docs/content/tutorial/step_08.ngdoc +++ b/docs/content/tutorial/step_08.ngdoc @@ -2,43 +2,31 @@ @name Tutorial: 8 - More Templating @description - <ul doc:tutorial-nav="8"></ul> - - In this step, you will implement the phone details view, which is displayed when a user clicks on a phone in the phone list. - - <doc:tutorial-instructions step="8"></doc:tutorial-instructions> - - Now when you click on a phone on the list, the phone details page with phone-specific information is displayed. - To implement the phone details view we will use {@link api/angular.service.$xhr $xhr} to fetch our data, and we'll flesh out the `phone-details.html` view template. - The most important changes are listed below. You can see the full diff on {@link https://github.com/angular/angular-phonecat/compare/step-7...step-8 GitHub}: - ## Data - In addition to `phones.json`, the `app/phones/` directory also contains one json file for each phone: - __`app/phones/nexus-s.json`:__ (sample snippet) <pre> { @@ -62,71 +50,53 @@ __`app/phones/nexus-s.json`:__ (sample snippet) </pre> - - Each of these files describes various properties of the phone using the same data structure. We'll show this data in the phone detail view. - - ## Controller - We'll expand the `PhoneDetailCtrl` by using the `$xhr` service to fetch the json files. This works the same way as the phone list controller. - __`app/js/controller.js`:__ <pre> function PhoneDetailCtrl($xhr) { var self = this; - $xhr('GET', 'phones/' + self.params.phoneId + '.json', function(code, response) { self.phone = response; }); } - //PhoneDetailCtrl.$inject = ['$xhr']; </pre> - To construct the URL for the HTTP request, we use `params.phoneId` extracted from the current route in the `PhoneCatCtrl` controller. - - ## Template - The TBD placeholder line has been replaced with lists and bindings that comprise the phone details. Note where we use the angular `{{expression}}` markup and `ng:repeater`s to project phone data from our model into the view. - - __`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> @@ -143,17 +113,13 @@ __`app/partials/phone-details.html`:__ </ul> </pre> - <img src="img/tutorial/tutorial_08-09_final.png"> - ## Test - We wrote a new unit test that is similar to the one we wrote for the `PhoneListCtrl` controller in step 5. - __`test/unit/controllerSpec.js`:__ <pre> ... @@ -162,46 +128,36 @@ __`test/unit/controllerSpec.js`:__ $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> - To run the unit tests, execute the `./scripts/test.sh` script and you should see the following output. - Chrome: Runner reset. ... Total 3 tests (Passed: 3; Fails: 0; Errors: 0) (5.00 ms) Chrome 11.0.696.57 Mac OS: Run 3 tests (Passed: 3; Fails: 0; Errors 0) (5.00 ms) - - We also added a new end-to-end test that navigates to the Nexus S detail page and verifies that the heading on the page is "Nexus S". - __`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'); }); @@ -210,33 +166,23 @@ __`test/e2e/scenarios.js`:__ </pre> - - You can now refresh the browser tab with the end-to-end test runner to see the tests run, or you can see them running on {@link http://angular.github.com/angular-phonecat/step-8/test/e2e/runner.html angular's server}. - # Experiments - * Using the {@link https://docs.google.com/document/d/11L8htLKrh6c92foV71ytYpiKkeKpM4_a5-9c3HywfIc/edit?hl=en&pli=1# end-to-end test runner API}, write a test that verifies that we display 4 thumbnail images on the Nexus S details page. - - # Summary - Now that the phone details view is in place, proceed to step 9 to learn how to write your own custom display filter. - - <ul doc:tutorial-nav="8"></ul> - diff --git a/docs/content/tutorial/step_09.ngdoc b/docs/content/tutorial/step_09.ngdoc index fc9ba360..0d5da766 100644 --- a/docs/content/tutorial/step_09.ngdoc +++ b/docs/content/tutorial/step_09.ngdoc @@ -2,44 +2,31 @@ @name Tutorial: 9 - Filters @description - <ul doc:tutorial-nav="9"></ul> - - In this step you will learn how to create your own custom display filter. - - <doc:tutorial-instructions step="9"></doc:tutorial-instructions> - - Navigate to one of the detail pages. - In the previous step, the details page displayed either "true" or "false" to indicate whether certain phone features were present or not. We have used a custom filter to convert those text strings into glyphs: ✓ for "true", and ✘ for "false". Let's see, what the filter code looks like. - The most important changes are listed below. You can see the full diff on {@link https://github.com/angular/angular-phonecat/compare/step-8...step-9 GitHub}: - - ## Custom Filter - In order to create a new filter, simply register your custom filter function with the {@link api/angular.filter `angular.filter`} API. - __`app/js/filters.js`:__ <pre> angular.filter('checkmark', function(input) { @@ -47,21 +34,16 @@ angular.filter('checkmark', function(input) { }); </pre> - The name of our filter is "checkmark". The `input` evaluates to either `true` or `false`, and we return one of two unicode characters we have chosen to represent true or false (`\u2713` and `\u2718`). - - ## Template - Since the filter code lives in the `app/js/filters.js` file, we need to include this file in our layout template. - __`app/index.html`:__ <pre> ... @@ -70,19 +52,14 @@ __`app/index.html`:__ ... </pre> - The syntax for using filters in angular templates is as follows: - {{ expression | filter }} - Let's employ the filter in the phone details template: - - __`app/partials/phone-detail.html`:__ <pre> ... @@ -96,19 +73,14 @@ __`app/partials/phone-detail.html`:__ </pre> - - ## Test - Filters, like any other component, should be tested and these tests are very easy to write. - __`test/unit/filtersSpec.js`:__ <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'); diff --git a/docs/content/tutorial/step_10.ngdoc b/docs/content/tutorial/step_10.ngdoc index 109f5b77..510eacd4 100644 --- a/docs/content/tutorial/step_10.ngdoc +++ b/docs/content/tutorial/step_10.ngdoc @@ -2,80 +2,59 @@ @name Tutorial: 10 - Event Handlers @description - <ul doc:tutorial-nav="10"></ul> - - In this step, you will add a clickable phone image swapper to the phone details page. - - <doc:tutorial-instructions step="10"></doc:tutorial-instructions> - - 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 at how we can do this with angular. - The most important changes are listed below. You can see the full diff on {@link https://github.com/angular/angular-phonecat/compare/step-9...step-10 GitHub}: - - ## Controller - __`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> - In the `PhoneDetailCtrl` controller, we created the `mainImageUrl` model property and set its default value to the first phone image url. - We also created a `setImage` controller method to change the value of `mainImageUrl`. - - ## Template - __`app/partials/phone-detail.html`:__ <pre> <img ng:src="{{mainImageUrl}}" class="phone"/> - ... - <ul class="phone-thumbs"> <li ng:repeat="img in phone.images"> <img ng:src="{{img}}" ng:click="setImage(img)"> @@ -84,53 +63,40 @@ __`app/partials/phone-detail.html`:__ ... </pre> - We bound the `ng:src` attribute of the large image to the `mainImageUrl` property. - We also registered an {@link api/angular.directive.ng:click `ng:click`} handler with thumbnail images. When a user clicks on one of the thumbnail images, the handler will use the `setImage` controller method to change the value of the `mainImageUrl` property to the url of the thumbnail image. - <img src="img/tutorial/tutorial_10-11_final.png"> - - ## Test - To verify this new feature, we added two end-to-end tests. One verifies that the main image is set to the first phone image by default. The second test clicks on several thumbnail images and verifies that the main image changed appropriately. - __`test/e2e/scenarios.js`:__ <pre> ... describe('Phone detail view', function() { - beforeEach(function() { browser().navigateTo('../../app/index.html#/phones/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'); }); @@ -138,51 +104,37 @@ __`test/e2e/scenarios.js`:__ }); </pre> - You can now refresh the browser tab with the end-to-end test runner to see the tests run, or you can see them running on {@link http://angular.github.com/angular-phonecat/step-8/test/e2e/runner.html angular's server}. - # Experiments - * Let's add a new controller method to `PhoneCatCtrl`: - this.hello(name) = function(name) { alert('Hello ' + (name || 'world') + '!'); } - and add: - <button ng:click="hello('Elmo')">Hello</button> - to the `index.html` template. - The controller methods are inherited between controllers/scopes, so you can use the same snippet in the `phone-list.html` template as well. - * Move the `hello` method from `PhoneCatCtrl` to `PhoneListCtrl` and you'll see that the button declared in `index.html` will stop working, while the one declared in the `phone-list.html` template remains operational. - - # Summary - With the phone image swapper in place, we're ready for step 11 (the last step!) to learn an even better way to fetch data. - - <ul doc:tutorial-nav="10"></ul> diff --git a/docs/content/tutorial/step_11.ngdoc b/docs/content/tutorial/step_11.ngdoc index 892342b5..e328aecd 100644 --- a/docs/content/tutorial/step_11.ngdoc +++ b/docs/content/tutorial/step_11.ngdoc @@ -2,42 +2,30 @@ @name Tutorial: 11 - REST and Custom Services @description - <ul doc:tutorial-nav="11"></ul> - - In this step, you will improve the way our app fetches data. - - <doc:tutorial-instructions step="11"></doc:tutorial-instructions> - - The last improvement we will make to our app is to 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 api/angular.service.$xhr $xhr} API, HTTP methods and URLs. - The most important changes are listed below. You can see the full diff on {@link https://github.com/angular/angular-phonecat/compare/step-10...step-11 GitHub}: - - ## Template - The custom service is defined in `app/js/services.js` so we need to include this file in our layout template: - __`app/index.html`.__ <pre> ... @@ -45,10 +33,8 @@ __`app/index.html`.__ ... </pre> - ## Service - __`app/js/services.js`.__ <pre> angular.service('Phone', function($resource) { @@ -58,36 +44,29 @@ __`app/js/services.js`.__ }); </pre> - We used the {@link api/angular.service} API to register a custom service. We passed in the name of the service - 'Phone' - and a factory function. The factory function is similar to a controller's constructor in that both can declare dependencies via function arguments. The Phone service declared a dependency on the `$resource` service. - The {@link api/angular.service.$resource `$resource`} service makes it easy to create a {@link http://en.wikipedia.org/wiki/Representational_State_Transfer RESTful} client with just a few lines of code. This client can then be used in our application, instead of the lower-level {@link api/angular.service.$xhr $xhr} service. - - ## Controller - We simplified our sub-controllers (`PhoneListCtrl` and `PhoneDetailCtrl`) by factoring out the lower-level {@link api/angular.service.$xhr $xhr} service, replacing it with a new service called `Phone`. Angular's {@link api/angular.service.$resource `$resource`} service is easier to use than {@link api/angular.service.$xhr $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. - __`app/js/controllers.js`.__ <pre> ... - function PhoneListCtrl(Phone_) { this.orderProp = 'age'; this.phones = Phone_.query(); @@ -95,40 +74,30 @@ function PhoneListCtrl(Phone_) { //PhoneListCtrl.$inject = ['Phone']; - - function PhoneDetailCtrl(Phone_) { var self = this; - self.phone = Phone_.get({phoneId: self.params.phoneId}, function(phone) { self.mainImageUrl = phone.images[0]; }); - ... } //PhoneDetailCtrl.$inject = ['Phone']; </pre> - Notice how in `PhoneListCtrl` we replaced: - $xhr('GET', 'phones/phones.json', function(code, response) { self.phones = response; }); - with: - this.phones = Phone_.query(); - This is a simple statement that we want to query for all phones. - An important thing to notice in the code above is that we don't pass any callback functions when invoking methods of our Phone service. Although it looks as if the result were returned synchronously, that is not the case at all. What is returned synchronously is a "future" — an @@ -136,22 +105,17 @@ object, which will be filled with data when the xhr response returns. Because of in angular, we can use this future and bind it to our template. Then, when the data arrives, the view will automatically update. - Sometimes, relying on the future object and data-binding alone is not sufficient to do everything we require, so in these cases, we can add a callback to process the server response. The `PhoneDetailCtrl` controller illustrates this by setting the `mainImageUrl` in a callback. - - ## Test - We have modified our unit tests to verify that our new service is issuing HTTP requests and processing them as expected. The tests also check that our controllers are interacting with the service correctly. - The {@link api/angular.service.$resource $resource} client augments the response object with methods for updating and deleting the resource. If we were to use the standard `toEqual` matcher, our tests would fail because the test values would not match the responses exactly. To solve the @@ -161,13 +125,10 @@ http://pivotal.github.com/jasmine/jsdoc/symbols/jasmine.Matchers.html Jasmine ma ignores methods. - - __`test/unit/controllersSpec.js`:__ <pre> describe('PhoneCat controllers', function() { - beforeEach(function(){ this.addMatchers({ toEqualData: function(expected) { @@ -176,92 +137,71 @@ 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).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> - To run the unit tests, execute the `./scripts/test.sh` script and you should see the following output. - Chrome: Runner reset. .... Total 4 tests (Passed: 4; Fails: 0; Errors: 0) (3.00 ms) Chrome 11.0.696.57 Mac OS: Run 4 tests (Passed: 4; Fails: 0; Errors 0) (3.00 ms) - - # Summary - There you have it! We have created a web app in a relatively short amount of time. - - <ul doc:tutorial-nav="11"></ul> - diff --git a/docs/content/tutorial/the_end.ngdoc b/docs/content/tutorial/the_end.ngdoc index fd96a920..ed6eda97 100644 --- a/docs/content/tutorial/the_end.ngdoc +++ b/docs/content/tutorial/the_end.ngdoc @@ -2,26 +2,20 @@ @name Tutorial: The End @description - Our application is now complete. Feel free to experiment with the code further, and jump back to previous steps using the `git checkout` or `goto_step.sh` commands. - 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 code, see the {@link cookbook/ Cookbook}. - When you are ready to start developing a project using angular, we recommend that you bootstrap your development with the {@link https://github.com/angular/angular-seed angular seed} project. - We hope this tutorial was useful to you and that you learned enough about angular to make you want to learn more. We especially hope you are inspired to go out and develop angular web apps of your own, and that you might be interested in {@link misc/contribute contributing} to angular. - If you have questions or feedback or just want to say "hi", please post a message at {@link https://groups.google.com/forum/#!forum/angular}. -- cgit v1.2.3