doc(guide): add concepts
467
docs/content/guide/concepts.ngdoc
Normal file
|
|
@ -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.
|
||||
BIN
docs/img/guide/concepts-controller.png
Normal file
|
After Width: | Height: | Size: 80 KiB |
BIN
docs/img/guide/concepts-directive.png
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
docs/img/guide/concepts-model.png
Normal file
|
After Width: | Height: | Size: 55 KiB |
BIN
docs/img/guide/concepts-module-injector.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
docs/img/guide/concepts-runtime.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
docs/img/guide/concepts-scope.png
Normal file
|
After Width: | Height: | Size: 78 KiB |
BIN
docs/img/guide/concepts-startup.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
docs/img/guide/concepts-view.png
Normal file
|
After Width: | Height: | Size: 51 KiB |
|
|
@ -58,10 +58,10 @@ exports.Example.prototype.addSource = function(name, content) {
|
|||
};
|
||||
|
||||
exports.Example.prototype.toHtml = function() {
|
||||
return '<h1>Source</h1>\n' +
|
||||
return '<h2>Source</h2>\n' +
|
||||
this.toHtmlEdit() +
|
||||
this.toHtmlTabs() +
|
||||
'<h1>Demo</h1>\n' +
|
||||
'<h2>Demo</h2>\n' +
|
||||
this.toHtmlEmbed();
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -145,6 +145,11 @@ ul.events > li > h3 {
|
|||
font-family: monospace;
|
||||
}
|
||||
|
||||
.center {
|
||||
display: block;
|
||||
margin: 2em auto;
|
||||
}
|
||||
|
||||
.diagram {
|
||||
display: block;
|
||||
margin: 2em auto;
|
||||
|
|
@ -175,3 +180,7 @@ ul.events > li > h3 {
|
|||
color: white;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.clear {
|
||||
clear: both;
|
||||
}
|
||||
|
|
|
|||
7483
images/docs/guide/concepts.graffle/data.plist
Normal file
BIN
images/docs/guide/concepts.graffle/image1.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
images/docs/guide/concepts.graffle/image4.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
images/docs/guide/concepts.graffle/image5.png
Normal file
|
After Width: | Height: | Size: 26 KiB |