diff options
| author | Misko Hevery | 2012-05-29 16:25:21 -0700 |
|---|---|---|
| committer | Misko Hevery | 2012-08-27 15:44:38 -0700 |
| commit | 7a5f25f6671eb5f51b06615d74a05855ab79f31e (patch) | |
| tree | acc28ac6cc3ec755531fa43147b06e5bd5b6c565 /docs/content | |
| parent | 96697f464fdce21480f52b4cf6d74a267058f63d (diff) | |
| download | angular.js-7a5f25f6671eb5f51b06615d74a05855ab79f31e.tar.bz2 | |
doc(guide): add concepts
Diffstat (limited to 'docs/content')
| -rw-r--r-- | docs/content/guide/concepts.ngdoc | 467 |
1 files changed, 467 insertions, 0 deletions
diff --git a/docs/content/guide/concepts.ngdoc b/docs/content/guide/concepts.ngdoc new file mode 100644 index 00000000..77d8c401 --- /dev/null +++ b/docs/content/guide/concepts.ngdoc @@ -0,0 +1,467 @@ +@ngdoc overview +@name Conceptual Overview +@description + +# Overview + +This document gives a quick overview of the main angular components and how they work together. +These are: + + * {@link concepts#startup startup} - bring up hello world + * {@link concepts#runtime runtime} - overview of angular runtime + * {@link concepts#scope scope} - the glue between the view and the controller + * {@link concepts#controller controller} - application behavior + * {@link concepts#model model} - your application data + * {@link concepts#view view} - what the user sees + * {@link concepts#directives directives} - extend HTML vocabulary + * {@link concepts#filters filters} - format the data in user locale + * {@link concepts#injector injector} - assembles your application + * {@link concepts#module module} - configures the injector + * {@link concepts#angular_namespace `$`} - angular namespace + +<a name="startup"></a> +# Startup + +This is how we get the ball rolling (refer to the diagram and example below): + +<img class="pull-right" style="padding-left: 3em;" src="img/guide/concepts-startup.png"> + + 1. Browser loads the HTML and parses it into a DOM + 2. Browser loads `angular.js` script + 3. Angular waits for `DOMContentLoaded` event + 4. Angular looks for {@link api/ng.directive:ngApp ng-app} + {@link guide/directive directive}, which designates application boundary + 5. {@link guide/module Module} specified in {@link + api/ng.directive:ngApp ng-app} (if any) is used to configure + the {@link api/AUTO.$injector $injector} + 6. {@link api/AUTO.$injector $injector} is used to create the {@link + api/ng.$compile $compile} service as well as {@link + api/ng.$rootScope $rootScope} + 7. {@link api/ng.$compile $compile} service is used to compile the DOM and link + it with {@link api/ng.$rootScope $rootScope} + 8. {@link api/ng.directive:ngInit ng-init} {@link + guide/directive directive} assigns `World` to the `name` property on the {@link guide/scope + scope} + 9. The `{{name}}` {@link api/ng.$interpolate interpolates} the expression to + `Hello World!` + +<div class="clear"> +</div> +<example> + <file name="index.html"> + <p ng-init=" name='World' ">Hello {{name}}!</p> + </file> +</example> + + +<a name="runtime"></a> +# Runtime + +<img class="pull-right" style="padding-left: 3em; padding-bottom: 1em;" src="img/guide/concepts-runtime.png"> + +The diagram and the example below describe how Angular interacts with browser's event loop. + + 1. Browsers event-loop waits for an event to arrive. Event is a user interactions, timer event, + or network event (response from a server). + 2. The events callback gets executed. This enters the JavaScript context. The callback can + modify the DOM structure. + 3. Once the callback finishes execution, the browser leaves the JavaScript context and + re-renders the view based on DOM changes. + +Angular modifies the normal JavaScript flow by providing it's own event processing loop. This +splits the JavaScript into classical and Angular execution context. Only operations which are +applied in Angular execution context will benefit from angular data-binding, exception handling, +property watching, etc... Use $apply() to enter Angular execution context from JavaScript. Keep in +mind that in most places (controllers, services) the $apply has already been called for you by the +directive which is handling the event. The need to call $apply is reserved only when +implementing custom event callbacks, or when working with a third-party library callbacks. + + 1. Enter Angular execution context by calling {@link guide/scope scope}`.`{@link + api/ng.$rootScope.Scope#$apply $apply}`(stimulusFn)`. Where `stimulusFn` is + the work you wish to do in Angular execution context. + 2. Angular executes the `stimulusFn()`, which typically modifies application state. + 3. Angular enters the {@link api/ng.$rootScope.Scope#$digest $digest} loop. The + loop is made up of two smaller loops which process {@link + api/ng.$rootScope.Scope#$evalAsync $evalAsync} queue and the {@link + api/ng.$rootScope.Scope#$watch $watch} list. The {@link + api/ng.$rootScope.Scope#$digest $digest} loop keeps iterating until the model + stabilizes, which means that the {@link api/ng.$rootScope.Scope#$evalAsync + $evalAsync} queue is empty and the {@link api/ng.$rootScope.Scope#$watch + $watch} list does not detect any changes. + 4. The {@link api/ng.$rootScope.Scope#$evalAsync $evalAsync} queue is used to + schedule work which needs to occur outside of current stack frame, but before the browser + view render. This is usually done with `setTimeout(0)`, but the `setTimeout(0)` approach + suffers from slowness and may cause view flickering since the browser renders the view after + each event. + 5. The {@link api/ng.$rootScope.Scope#$watch $watch} list is a set of expressions + which may have changed since last iteration. If a change is detected then the `$watch` + function is called which typically updates the DOM with the new value. + 6. Once Angular {@link api/ng.$rootScope.Scope#$digest $digest} loop finishes + the execution leaves the Angular and JavaScript context. This is followed by the browser + re-rendering the DOM to reflect any changes. + + +Here is the explanation of how the `Hello wold` example achieves the data-binding effect when the +user enters text into the text field. + + 1. During the compilation phase: + 1. the {@link api/ng.directive:ngModel ng-model} and {@link + api/ng.directive:input input} {@link guide/directive + directive} set up a `keydown` listener on the `<input>` control. + 2. the {@link api/ng.$interpolate {{name}} } interpolation + sets up a {@link api/ng.$rootScope.Scope#$watch $watch} to be notified of + `name` changes. + 2. During the runtime phase: + 1. Pressing an '`X`' key causes the browser to emit a `keydown` event on the input control. + 2. The {@link api/ng.directive:input input} directive + captures the change to the input's value and calls {@link + api/ng.$rootScope.Scope#$apply $apply}`("name = 'X';")` to update the + application model inside the Angular execution context. + 3. Angular applies the `name = 'X';` to the model. + 4. The {@link api/ng.$rootScope.Scope#$digest $digest} loop begins + 5. The {@link api/ng.$rootScope.Scope#$watch $watch} list detects a change + on the `name` property and notifies the {@link api/ng.$interpolate + {{name}} } interpolation, which in turn updates the DOM. + 6. Angular exits the execution context, which in turn exits the `keydown` event and with it + the JavaScript execution context. + 7. The browser re-renders the view with update text. + +<div class="clear"> +</div> +<example> + <file name="index.html"> + <input ng-model="name"> + <p>Hello {{name}}!</p> + </file> +</example> + +<a name="scope"></a> +#Scope + +The {@link guide/scope scope} is responsible for detecting changes to the model section and +provides the execution context for expressions. The scopes are nested in a hierarchical structure +which closely follow the DOM structure. (See individual directive documentation to see which +directives cause a creation of new scopes.) + +The following example demonstrates how `name` {@link guide/expression expression} will evaluate +into different value depending on which scope it is evaluated in. The example is followed by +a diagram depicting the scope boundaries. + +<div class="clear"> +</div> +<div class="show-scope"> +<example> + <file name="index.html"> + <div ng-controller="GreetCtrl"> + Hello {{name}}! + </div> + <div ng-controller="ListCtrl"> + <ol> + <li ng-repeat="name in names">{{name}}</li> + </ol> + </div> + </file> + <file name="script.js"> + function GreetCtrl($scope) { + $scope.name = 'World'; + } + + function ListCtrl($scope) { + $scope.names = ['Igor', 'Misko', 'Vojta']; + } + </file> + <file name="style.css"> + .show-scope .doc-example-live.ng-scope, + .show-scope .doc-example-live .ng-scope { + border: 1px solid red; + margin: 3px; + } + </file> +</example> +</div> + +<img class="center" src="img/guide/concepts-scope.png"> + + +<a name="controller"></a> +# Controller + +<img class="pull-right" style="padding-left: 3em; padding-bottom: 1em;" src="img/guide/concepts-controller.png"> + +Controller is the code behind the view. Its job is to construct the model and publish it to the +view along with callback methods. The view is a projection of the scope onto the template (the +HTML). The scope is the glue which marshals the model to the view and forwards the events to the +controller. + +The separation of the controller and the view is important because: + + * The controller is written in JavaScript. JavaScript is imperative. Imperative is a good fit + for specifying application behavior. The controller should not contain any rendering + information (DOM references or HTML fragments). + * The view template is written in HTML. HTML is declarative. Declarative is a good fit for + specifying UI. The View should not contain any behavior. + * Since the controller is unaware of the view, there could be many views for the same + controller. This is important for re-skinning, device specific views (i.e. mobile vs desktop), + and testability. + +<div class="clear"> +</div> +<example> + <file name="index.html"> + <div ng-controller="MyCtrl"> + Hello {{name}}! + <button ng-click="action()"> + OK + </button> + </div> + </file> + <file name="script.js"> + function MyCtrl($scope) { + $scope.action = function() { + $scope.name = 'OK'; + } + + $scope.name = 'World'; + } + </file> +</example> + + +<a name="model"></a> +# Model + +<img class="pull-right" style="padding-left: 3em; padding-bottom: 1em;" src="img/guide/concepts-model.png"> + +The model is the data which is used merged with the template to produce the view. To be able to +render the model into the view, the model has to be referenceable from the scope. Unlike many +other frameworks Angular makes no restrictions or requirements an the model. There are no classes +to inherit from or special accessor methods for accessing or changing the model. The model can be +primitive, object hash, or a full object Type. In short the model is a plain JavaScript object. + + +<div class="clear"> +</div> + + +<a name="view"></a> +# View + +<img class="pull-right" style="padding-left: 3em; padding-bottom: 1em;" src="img/guide/concepts-view.png"> + +The view is what the users sees. The view begins its life as a template, it is merged with the +model and finally rendered into the browser DOM. Angular takes a very different approach to +rendering the view, to most other templating systems. + + * **Others** - Most templating systems begin as an HTML string with special templating markup. + Often the template markup breaks the HTML syntax which means that the template can not be + edited by an HTML editor. The template string is then parsed by the template engine, and + merged with the data. The result of the merge is an HTML string. The HTML string is then + written to the browser using the `.innerHTML`, which causes the browser to render the HTML. + When the model changes the whole process needs to be repeated. The granularity of the template + is the granularity of the DOM updates. The key here is that the templating system manipulates + strings. + * **Angular** - Angular is different, since its templating system works on DOM objects not on + strings. The template is still written in HTML string, but it is HTML (not HTML with + template sprinkled in.) The browser parses the HTML into DOM, and the DOM becomes the input to + the template engine know as the {@link api/ng.$compile compiler}. The compiler + looks for {@link guide/directive directives} which in turn set up {@link + api/ng.$rootScope.Scope#$watch watches} on the model. The result is a + continuously updating view which does not need template model re-merging. Your model becomes + the single source-of-truth for your view. + +<div class="clear"> +</div> + +<example> + <file name="index.html"> + <div ng-init="list = ['Chrome', 'Safari', 'Firefox', 'IE'] "> + <input ng-model="list" ng-list> <br> + <input ng-model="list" ng-list> <br> + <pre>list={{list}}</pre> <br> + <ol> + <li ng-repeat="item in list"> + {{item}} + </li> + </ol> + </div> + </file> +</example> + + +<a name="directives"></a> +# Directives + +A directive is a behavior or DOM transformation which is triggered by a presence of an attribute, +element name, or a class name. A directive allows you to extend the HTML vocabulary in a +declarative fashion. Following is an example which enables data-binding for the `contenteditable` +in HTML. + +<example module="directive"> + <file name="script.js"> + angular.module('directive', []).directive('contenteditable', function() { + return { + require: 'ngModel', + link: function(scope, elm, attrs, ctrl) { + // view -> model + elm.bind('blur', function() { + scope.$apply(function() { + ctrl.$setViewValue(elm.html()); + }); + }); + + // model -> view + ctrl.render = function(value) { + elm.html(value); + }; + + // load init value from DOM + ctrl.$setViewValue(elm.html()); + } + }; + }); + </file> + <file name="index.html"> + <div contentEditable="true" ng-model="content">Edit Me</div> + <pre>model = {{content}}</pre> + </file> + <file name="style.css"> + div[contentEditable] { + cursor: pointer; + background-color: #D0D0D0; + margin-bottom: 1em; + padding: 1em; + } + </file> +</example> + +<a name="filters"></a> +# Filters + +{@link api/ng.$filter Filters} perform data transformation roles. Typically +they are used in conjunction with the locale to format the data in locale specific output. +They are follow the spirit of UNIX filters and follow similar syntax `|` (pipe). + +<example> + <file name="index.html"> + <div ng-init="list = ['Chrome', 'Safari', 'Firefox', 'IE'] "> + Number formatting: {{ 1234567890 | number }} <br> + array filtering <input ng-model="predicate"> + {{ list | filter:predicate | json }} + </div> + </file> +</example> + + +<a name="module"></a> +<a name="injector"></a> +# Modules and the Injector + +<img class="pull-right" style="padding-left: 3em; padding-bottom: 1em;" src="img/guide/concepts-module-injector.png"> + +An {@link api/AUTO.$injector injector} is a service locator. There is a single +{@link api/AUTO.$injector injector} per Angular {@link +api/ng.directive:ngApp application}. The {@link +api/AUTO.$injector injector} provides a way to look up an object instance by its +name. The injector keeps on internal cache of all objects so that repeated calls to get the same +object name result in the same instance. If the object does not exist, then the {@link +api/AUTO.$injector injector} asks the instance factory to create a new instance. + +A {@link api/angular.Module module} is a way to configure the injector's instance factory, known +as a {@link api/AUTO.$provide provider}. + +<div class='clear'></div> +<pre> + // Create a module + var myModule = angular.module('myModule', []) + + // Configure the injector + myModule.factory('serviceA', function() { + return { + // instead of {}, put your object creation here + }; + }); + + // create an injector and configure it from 'myModule' + var $injector = angular.injector('myModule'); + + // retrieve an object from the injector by name + var serviceA = $injector.get('serviceA'); + + // always true because of instance cache + $injector.get('serviceA') === $injector.get('serviceA'); +</pre> + + +But the real magic of the {@link api/AUTO.$injector injector} is that it can be +used to {@link api/AUTO.$injector#invoke call} methods and {@link +api/AUTO.$injector#instantiate instantiate} types. This subtle feature is what +allows the methods and types to ask for their dependencies rather then to look for them. + +<pre> + // You write functions such as this one. + function doSomething(serviceA, serviceB) { + // do something here. + } + + // Angular provides the injector for your application + var $injector = ...; + + /////////////////////////////////////////////// + // the old-school way of getting dependencies. + var serviceA = $injector.get('serviceA'); + var serviceB = $injector.get('serviceB'); + + // now call the function + doSomething(serviceA, serviceB); + + /////////////////////////////////////////////// + // the cool way of getting dependencies. + // the $injector will supply the arguments to the function automatically + $injector.invoke(doSomething); // This is how the framework calls your functions +</pre> + +Notice that the only thing you needed to write was the function, and list the dependencies in the +function arguments. When angular calls the function, it will use the {@link +api/AUTO.$injector#invoke call} which will automatically fill the function +arguments. + +Examine the `ClockCtrl` bellow, and notice how it list the dependencies in constructor. When the +{@link api/ng.directive:ngController ng-controller} instantiates +the controller it automatically provides the dependencies. There is no need to create +dependencies, look for dependencies, or even get a reference to the injector. + +<example module="timeExampleModule"> + <file name="index.html"> + <div ng-controller="ClockCtrl"> + Current time is: {{ time.now }} + </div> + </file> + <file name="script.js"> + angular.module('timeExampleModule', []). + // Declare new object call time, + // which will be available for injection + factory('time', function($timeout) { + var time = {}; + + (function tick() { + time.now = new Date().toString(); + $timeout(tick, 1000); + })(); + return time; + }); + + // Notice that you can simply ask for time + // and it will be provided. No need to look for it. + function ClockCtrl($scope, time) { + $scope.time = time; + } + </file> +</example> + + +<a name="angular_namespace"></a> +# Angular Namespace + +To prevent accidental name collision, Angular prefixes names of objects which could potentially +collide with `$`. Please do not use the `$` prefix in your code as it may accidentally collide +with Angular code. |
