docs(guide/directive,guide/compiler,): drastically improve

This commit is contained in:
Brian Ford 2013-10-23 14:04:47 -07:00
parent 32ab648c79
commit e69c287293
6 changed files with 1422 additions and 703 deletions

View file

@ -3,7 +3,7 @@
@fullName Missing Required Controller
@description
This error occurs when {@link api/ng.$compile template compiler} tries process the directive that specifies the `require` option in a {@link guide/directive#writing-directives_directive-definition-object directive definition},
This error occurs when {@link api/ng.$compile HTML compiler} tries to process a directive that specifies the {@link api/ng.$compile#description_comprehensive-directive-api_directive-definition-object `require` option} in a {@link api/ng.$compile#description_comprehensive-directive-api directive definition},
but the required directive controller is not present on the current DOM element (or its ancestor element, if `^` was specified).
To resolve this error ensure that there is no typo in the required controller name and that the required directive controller is present on the current element.

View file

@ -21,4 +21,5 @@ myModule.directive('directiveName', function factory() {
});
```
Please refer to the {@link guide/directive#writing-directives_directive-definition-object directive definition docs} to learn more about the api.
Please refer to the {@link api/ng.$compile#description_comprehensive-directive-api_directive-definition-object
`scope` option} of the directive definition documentation to learn more about the API.

View file

@ -3,7 +3,9 @@
@fullName Non-Assignable Expression
@description
This error occurs when a directive defines an isolate scope property that support two-way data-binding (using the `=` mode in the {@link guide/directive#writing-directives_directive-definition-object directive definition}) but the directive is used with an expression that is not-assignable.
This error occurs when a directive defines an isolate scope property
(using the `=` mode in the {@link api/ng.$compile#description_comprehensive-directive-api_directive-definition-object
`scope` option} of a directive definition) but the directive is used with an expression that is not-assignable.
In order for the two-way data-binding to work, it must be possible to write new values back into the path defined with the expression.

View file

@ -2,6 +2,15 @@
@name Developer Guide: HTML Compiler
@description
<div class="alert alert-warning">
**Note:** this guide is targeted towards developers who are already familiar with AngularJS basics.
If you're just getting started, we recommend the {@link tutorial/ tutorial} first.
If you just want to create custom directives, we recommend the {@link guide/directive directives guide}.
If you want a deeper look into Angular's compilation process, you're in the right place.
</div>
# Overview
Angular's {@link api/ng.$compile HTML compiler} allows the developer to teach the
@ -27,9 +36,9 @@ All of this compilation takes place in the web browser; no server side or pre-co
involved.
# Compiler
## Compiler
Compiler is an angular service which traverses the DOM looking for attributes. The compilation
Compiler is an Angular service which traverses the DOM looking for attributes. The compilation
process happens in two phases.
1. **Compile:** traverse the DOM and collect all of the directives. The result is a linking
@ -44,7 +53,7 @@ for each item in a collection. Having a compile and link phase improves performa
cloned template only needs to be compiled once, and then linked once for each clone instance.
# Directive
## Directive
A directive is a behavior which should be triggered when specific HTML constructs are encountered
during the compilation process. The directives can be placed in element names, attributes, class
@ -108,35 +117,260 @@ Here is a directive which makes any element draggable. Notice the `draggable` at
</example>
The presence of the `draggable` attribute on any element gives the element new behavior. The beauty of
this approach is that we have taught the browser a new trick. We have extended the vocabulary of
what the browser understands in a way which is natural to anyone who is familiar with HTML
principles.
The presence of the `draggable` attribute on any element gives the element new behavior.
We extended the vocabulary of the browser in a way which is natural to anyone who is familiar with the principles of HTML.
# Understanding View
## Understanding View
There are many templating systems out there. Most of them consume a static string template and
Most other templating systems consume a static string template and
combine it with data, resulting in a new string. The resulting text is then `innerHTML`ed into
an element.
<img src="img/One_Way_Data_Binding.png">
This means that any changes to the data need to be re-merged with the template and then
`innerHTML`ed into the DOM. Some of the issues with this approach are: reading user input and merging it with data,
clobbering user input by overwriting it, managing the whole update process, and lack of behavior
expressiveness.
`innerHTML`ed into the DOM. Some of the issues with this approach are:
Angular is different. The Angular compiler consumes the DOM with directives, not string templates.
1. reading user input and merging it with data
2. clobbering user input by overwriting it
3. managing the whole update process
4. lack of behavior expressiveness
Angular is different. The Angular compiler consumes the DOM, not string templates.
The result is a linking function, which when combined with a scope model results in a live view. The
view and scope model bindings are transparent. No action from the developer is needed to update
the view. And because no `innerHTML` is used there are no issues of clobbering user input.
view and scope model bindings are transparent. The developer does not need to make any special calls to update
the view. And because `innerHTML` is not used, you won't accidentally clobber user input.
Furthermore, Angular directives can contain not just text bindings, but behavioral constructs as
well.
<img src="img/Two_Way_Data_Binding.png">
The Angular approach produces a stable DOM. This means that the DOM element instance bound to a model
The Angular approach produces a stable DOM. The DOM element instance bound to a model
item instance does not change for the lifetime of the binding. This means that the code can get
hold of the elements and register event handlers and know that the reference will not be destroyed
by template data merge.
## How directives are compiled
It's important to note that Angular operates on DOM nodes rather than strings. Usually, you don't
notice this restriction because when a page loads, the web browser parses HTML into the DOM automatically.
However it's important to keep this in mind when calling `$compile` yourself, because passing it a string
will fail. Instead, use `angular.element` to convert a string to DOM before passing elements into
Angular's `$compile` service.
HTML compilation happens in three phases:
1. {@link api/ng.$compile `$compile`} traverses the DOM and matches directives.
If the compiler finds than an element matches a directive, then the directive is added to the list of
directives that match the DOM element. A single element may match multiple directives.
2. Once all directives matching a DOM element have been identified, the compiler sorts the directives
by their `priority`.
Each directive's `compile` functions are executed. Each `compile` function has a chance to
modify the DOM. Each `compile` function returns a `link` function. These functions are composed into
a "combined" link function, which invokes each directive's returned `link` function.
3. `$compile` links the template with the scope by calling the combined linking function from the previous step.
This in turn will call the linking function of the individual directives, registering listeners on the elements
and setting up {@link api/ng.$rootScope.Scope#methods_$watch `$watch`s} with the {@link api/ng.$rootScope.Scope `scope`}
as each directive is configured to do.
The result of this is a live binding between the scope and the DOM. So at this point, a change in
a model on the compiled scope will be reflected in the DOM.
Below is the corresponding code using the `$compile` service.
This should help give you an idea of what Angular does internally.
<pre>
var $compile = ...; // injected into your code
var scope = ...;
var html = '<div ng-bind="exp"></div>';
// Step 1: parse HTML into DOM element
var template = angular.element(html);
// Step 2: compile the template
var linkFn = $compile(template);
// Step 3: link the compiled template with the scope.
linkFn(scope);
</pre>
### The difference between Compile and Link
At this point you may wonder why the compile process has separate compile and link phases. The
short answer is that compile and link separation is needed any time a change in a model causes
a change in the **structure** of the DOM.
It's rare for directives to have a **compile function**, since most directives are concerned with
working with a specific DOM element instance rather than changing its overall structure.
Directives often have a **link function**. A link function allows the directive to register
listeners to the specific cloned DOM element instance as well as to copy content into the DOM
from the scope.
<div class="alert alert-success">
**Best Practice:** Any operation which can be shared among the instance of directives should be
moved to the compile function for performance reasons.
</div>
#### An Example of "Compile" Versus "Link"
To understand, let's look at a real-world example with `ngRepeat`:
<pre>
Hello {{user}}, you have these actions:
<ul>
<li ng-repeat="action in user.actions">
{{action.description}}
</li>
</ul>
</pre>
When the above example is compiled, the compiler visits every node and looks for directives.
`{{user}}` matches the {@link api/ng.$interpolate interpolation directive}
and `ng-repeat` matches the {@link api/ng.directive:ngRepeat `ngRepeat` directive}.
But {@link api/ng.directive:ngRepeat ngRepeat} has a dilemma.
It needs to be able to clone new `<li>` elements for every `action` in `user.actions`.
This initially seems trivial, but it becomes more complicated when you consider that `user.actions`
might have items added to it later. This means that it needs to save a clean copy of the `<li>`
element for cloning purposes.
As new `action`s are inserted, the template `<li>` element needs to be cloned and inserted into `ul`.
But cloning the `<li>` element is not enough. It also needs to compile the `<li>` so that its
directives, like `{{action.description}}`, evaluate against the right {@link api/ng.$rootScope.Scope scope}.
A naive approach to solving this problem would be to simply insert a copy of the `<li>` element and
then compile it.
The problem with this approach is that compiling on every `<li>` element that we clone would duplicate
a lot of the work. Specifically, we'd be traversing `<li>` each time before cloning it to find the
directives. This would cause the compilation process to be slower, in turn making applications
less responsive when inserting new nodes.
The solution is to break the compilation process into two phases:
the **compile phase** where all of the directives are identified and sorted by priority,
and a **linking phase** where any work which "links" a specific instance of the
{@link api/ng.$rootScope.Scope scope} and the specific instance of an `<li>` is performed.
<div class="alert alert-warning">
**Note:** *Link* means setting up listeners on the DOM and setting up `$watch` on the Scope to
keep the two in sync.
</div>
{@link api/ng.directive:ngRepeat `ngRepeat`} works by preventing the compilation process from
descending into the `<li>` element so it can make a clone of the original and handle inserting
and removing DOM nodes itself.
Instead the {@link api/ng.directive:ngRepeat `ngRepeat`} directive compiles `<li>` separately.
The result of the `<li>` element compilation is a linking function which contains all of the
directives contained in the `<li>` element, ready to be attached to a specific clone of the `<li>`
element.
At runtime the {@link api/ng.directive:ngRepeat `ngRepeat`} watches the expression and as items
are added to the array it clones the `<li>` element, creates a new
{@link api/ng.$rootScope.Scope scope} for the cloned `<li>` element and calls the link function
on the cloned `<li>`.
### Understanding How Scopes Work with Transclused Directives
One of the most common use cases for directives is to create reusable components.
Below is a pseudo code showing how a simplified dialog component may work.
<pre>
<div>
<button ng-click="show=true">show</button>
<dialog title="Hello {{username}}."
visible="show"
on-cancel="show = false"
on-ok="show = false; doSomething()">
Body goes here: {{username}} is {{title}}.
</dialog>
</div>
</pre>
Clicking on the "show" button will open the dialog. The dialog will have a title, which is
data bound to `username`, and it will also have a body which we would like to transclude
into the dialog.
Here is an example of what the template definition for the `dialog` widget may look like.
<pre>
<div ng-show="visible">
<h3>{{title}}</h3>
<div class="body" ng-transclude></div>
<div class="footer">
<button ng-click="onOk()">Save changes</button>
<button ng-click="onCancel()">Close</button>
</div>
</div>
</pre>
This will not render properly, unless we do some scope magic.
The first issue we have to solve is that the dialog box template expects `title` to be defined, but
the place of instantiation would like to bind to `username`. Furthermore the buttons expect the
`onOk` and `onCancel` functions to be present in the scope. This limits the usefulness of the
widget. To solve the mapping issue we use the `locals` to create local variables which the template
expects as follows:
<pre>
scope: {
title: '@', // the title uses the data-binding from the parent scope
onOk: '&', // create a delegate onOk function
onCancel: '&', // create a delegate onCancel function
visible: '=' // set up visible to accept data-binding
}
</pre>
Creating local properties on widget scope creates two problems:
1. isolation - if the user forgets to set `title` attribute of the dialog widget the dialog
template will bind to parent scope property. This is unpredictable and undesirable.
2. transclusion - the transcluded DOM can see the widget locals, which may overwrite the
properties which the transclusion needs for data-binding. In our example the `title`
property of the widget clobbers the `title` property of the transclusion.
To solve the issue of lack of isolation, the directive declares a new `isolated` scope. An
isolated scope does not prototypically inherit from the child scope, and therefore we don't have
to worry about accidentally clobbering any properties.
However `isolated` scope creates a new problem: if a transcluded DOM is a child of the widget
isolated scope then it will not be able to bind to anything. For this reason the transcluded scope
is a child of the original scope, before the widget created an isolated scope for its local
variables. This makes the transcluded and widget isolated scope siblings.
This may seem to be unexpected complexity, but it gives the widget user and developer the least
surprise.
Therefore the final directive definition looks something like this:
<pre>
transclude: true,
scope: {
title: '@', // the title uses the data-binding from the parent scope
onOk: '&', // create a delegate onOk function
onCancel: '&', // create a delegate onCancel function
visible: '=' // set up visible to accept data-binding
},
restrict: 'E',
replace: true
</pre>

File diff suppressed because it is too large Load diff

View file

@ -25,21 +25,355 @@
*
* @description
* Compiles a piece of HTML string or DOM into a template and produces a template function, which
* can then be used to link {@link ng.$rootScope.Scope scope} and the template together.
* can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together.
*
* The compilation is a process of walking the DOM tree and trying to match DOM elements to
* {@link ng.$compileProvider#methods_directive directives}. For each match it
* executes corresponding template function and collects the
* instance functions into a single template function which is then returned.
* The compilation is a process of walking the DOM tree and matching DOM elements to
* {@link ng.$compileProvider#methods_directive directives}.
*
* The template function can then be used once to produce the view or as it is the case with
* {@link ng.directive:ngRepeat repeater} many-times, in which
* case each call results in a view that is a DOM clone of the original template.
* <div class="alert alert-warning">
* **Note:** This document is an in-depth reference of all directive options.
* For a gentle introduction to directives with examples of common use cases,
* see the {@link guide/directive directive guide}.
* </div>
*
* ## Comprehensive Directive API
*
* There are many different options for a directive.
*
* The difference resides in the return value of the factory function.
* You can either return a "Directive Definition Object" (see below) that defines the directive properties,
* or just the `postLink` function (all other properties will have the default values).
*
* <div class="alert alert-success">
* **Best Practice:** It's recommended to use the "directive definition object" form.
* </div>
*
* Here's an example directive declared with a Directive Definition Object:
*
* <pre>
* var myModule = angular.module(...);
*
* myModule.directive('directiveName', function factory(injectables) {
* var directiveDefinitionObject = {
* priority: 0,
* template: '<div></div>', // or // function(tElement, tAttrs) { ... },
* // or
* // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... },
* replace: false,
* transclude: false,
* restrict: 'A',
* scope: false,
* controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
* require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'],
* compile: function compile(tElement, tAttrs, transclude) {
* return {
* pre: function preLink(scope, iElement, iAttrs, controller) { ... },
* post: function postLink(scope, iElement, iAttrs, controller) { ... }
* }
* // or
* // return function postLink( ... ) { ... }
* },
* // or
* // link: {
* // pre: function preLink(scope, iElement, iAttrs, controller) { ... },
* // post: function postLink(scope, iElement, iAttrs, controller) { ... }
* // }
* // or
* // link: function postLink( ... ) { ... }
* };
* return directiveDefinitionObject;
* });
* </pre>
*
* <div class="alert alert-warning">
* **Note:** Any unspecified options will use the default value. You can see the default values below.
* </div>
*
* Therefore the above can be simplified as:
*
* <pre>
* var myModule = angular.module(...);
*
* myModule.directive('directiveName', function factory(injectables) {
* var directiveDefinitionObject = {
* link: function postLink(scope, iElement, iAttrs) { ... }
* };
* return directiveDefinitionObject;
* // or
* // return function postLink(scope, iElement, iAttrs) { ... }
* });
* </pre>
*
*
*
* ### Directive Definition Object
*
* The directive definition object provides instructions to the {@link api/ng.$compile
* compiler}. The attributes are:
*
* #### `priority`
* When there are multiple directives defined on a single DOM element, sometimes it
* is necessary to specify the order in which the directives are applied. The `priority` is used
* to sort the directives before their `compile` functions get called. Priority is defined as a
* number. Directives with greater numerical `priority` are compiled first. The order of directives with
* the same priority is undefined. The default priority is `0`.
*
* #### `terminal`
* If set to true then the current `priority` will be the last set of directives
* which will execute (any directives at the current priority will still execute
* as the order of execution on same `priority` is undefined).
*
* #### `scope`
* **If set to `true`,** then a new scope will be created for this directive. If multiple directives on the
* same element request a new scope, only one new scope is created. The new scope rule does not
* apply for the root of the template since the root of the template always gets a new scope.
*
* **If set to `{}` (object hash),** then a new "isolate" scope is created. The 'isolate' scope differs from
* normal scope in that it does not prototypically inherit from the parent scope. This is useful
* when creating reusable components, which should not accidentally read or modify data in the
* parent scope.
*
* The 'isolate' scope takes an object hash which defines a set of local scope properties
* derived from the parent scope. These local properties are useful for aliasing values for
* templates. Locals definition is a hash of local scope property to its source:
*
* * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is
* always a string since DOM attributes are strings. If no `attr` name is specified then the
* attribute name is assumed to be the same as the local name.
* Given `<widget my-attr="hello {{name}}">` and widget definition
* of `scope: { localName:'@myAttr' }`, then widget scope property `localName` will reflect
* the interpolated value of `hello {{name}}`. As the `name` attribute changes so will the
* `localName` property on the widget scope. The `name` is read from the parent scope (not
* component scope).
*
* * `=` or `=attr` - set up bi-directional binding between a local scope property and the
* parent scope property of name defined via the value of the `attr` attribute. If no `attr`
* name is specified then the attribute name is assumed to be the same as the local name.
* Given `<widget my-attr="parentModel">` and widget definition of
* `scope: { localModel:'=myAttr' }`, then widget scope property `localModel` will reflect the
* value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected
* in `localModel` and any changes in `localModel` will reflect in `parentModel`. If the parent
* scope property doesn't exist, it will throw a NON_ASSIGNABLE_MODEL_EXPRESSION exception. You
* can avoid this behavior using `=?` or `=?attr` in order to flag the property as optional.
*
* * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope.
* If no `attr` name is specified then the attribute name is assumed to be the same as the
* local name. Given `<widget my-attr="count = count + value">` and widget definition of
* `scope: { localFn:'&myAttr' }`, then isolate scope property `localFn` will point to
* a function wrapper for the `count = count + value` expression. Often it's desirable to
* pass data from the isolated scope via an expression and to the parent scope, this can be
* done by passing a map of local variable names and values into the expression wrapper fn.
* For example, if the expression is `increment(amount)` then we can specify the amount value
* by calling the `localFn` as `localFn({amount: 22})`.
*
*
*
* #### `controller`
* Controller constructor function. The controller is instantiated before the
* pre-linking phase and it is shared with other directives (see
* `require` attribute). This allows the directives to communicate with each other and augment
* each other's behavior. The controller is injectable (and supports bracket notation) with the following locals:
*
* * `$scope` - Current scope associated with the element
* * `$element` - Current element
* * `$attrs` - Current attributes object for the element
* * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope:
* `function(cloneLinkingFn)`.
*
*
* #### `require`
* Require another directive and inject its controller as the fourth argument to the linking function. The
* `require` takes a string name (or array of strings) of the directive(s) to pass in. If an array is used, the
* injected argument will be an array in corresponding order. If no such directive can be
* found, or if the directive does not have a controller, then an error is raised. The name can be prefixed with:
*
* * (no prefix) - Locate the required controller on the current element. Throw an error if not found.
* * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found.
* * `^` - Locate the required controller by searching the element's parents. Throw an error if not found.
* * `?^` - Attempt to locate the required controller by searching the element's parentsor pass `null` to the
* `link` fn if not found.
*
*
* #### `controllerAs`
* Controller alias at the directive scope. An alias for the controller so it
* can be referenced at the directive template. The directive needs to define a scope for this
* configuration to be used. Useful in the case when directive is used as component.
*
*
* #### `restrict`
* String of subset of `EACM` which restricts the directive to a specific directive
* declaration style. If omitted, the default (attributes only) is used.
*
* * `E` - Element name: `<my-directive></my-directive>`
* * `A` - Attribute (default): `<div my-directive="exp"></div>`
* * `C` - Class: `<div class="my-directive: exp;"></div>`
* * `M` - Comment: `<!-- directive: my-directive exp -->`
*
*
* #### `template`
* replace the current element with the contents of the HTML. The replacement process
* migrates all of the attributes / classes from the old element to the new one. See the
* {@link guide/directive#creating-custom-directives_creating-directives_template-expanding-directive
* Directives Guide} for an example.
*
* You can specify `template` as a string representing the template or as a function which takes
* two arguments `tElement` and `tAttrs` (described in the `compile` function api below) and
* returns a string value representing the template.
*
*
* #### `templateUrl`
* Same as `template` but the template is loaded from the specified URL. Because
* the template loading is asynchronous the compilation/linking is suspended until the template
* is loaded.
*
* You can specify `templateUrl` as a string representing the URL or as a function which takes two
* arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns
* a string value representing the url. In either case, the template URL is passed through {@link
* api/ng.$sce#methods_getTrustedResourceUrl $sce.getTrustedResourceUrl}.
*
*
* #### `replace`
* specify where the template should be inserted. Defaults to `false`.
*
* * `true` - the template will replace the current element.
* * `false` - the template will replace the contents of the current element.
*
*
* #### `transclude`
* compile the content of the element and make it available to the directive.
* Typically used with {@link api/ng.directive:ngTransclude
* ngTransclude}. The advantage of transclusion is that the linking function receives a
* transclusion function which is pre-bound to the correct scope. In a typical setup the widget
* creates an `isolate` scope, but the transclusion is not a child, but a sibling of the `isolate`
* scope. This makes it possible for the widget to have private state, and the transclusion to
* be bound to the parent (pre-`isolate`) scope.
*
* * `true` - transclude the content of the directive.
* * `'element'` - transclude the whole element including any directives defined at lower priority.
*
*
* #### `compile`
*
* <pre>
* function compile(tElement, tAttrs, transclude) { ... }
* </pre>
*
* The compile function deals with transforming the template DOM. Since most directives do not do
* template transformation, it is not used often. Examples that require compile functions are
* directives that transform template DOM, such as {@link
* api/ng.directive:ngRepeat ngRepeat}, or load the contents
* asynchronously, such as {@link api/ngRoute.directive:ngView ngView}. The
* compile function takes the following arguments.
*
* * `tElement` - template element - The element where the directive has been declared. It is
* safe to do template transformation on the element and child elements only.
*
* * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared
* between all directive compile functions.
*
* * `transclude` - A transclude linking function: `function(scope, cloneLinkingFn)`.
*
* <div class="alert alert-warning">
* **Note:** The template instance and the link instance may be different objects if the template has
* been cloned. For this reason it is **not** safe to do anything other than DOM transformations that
* apply to all cloned DOM nodes within the compile function. Specifically, DOM listener registration
* should be done in a linking function rather than in a compile function.
* </div>
*
* A compile function can have a return value which can be either a function or an object.
*
* * returning a (post-link) function - is equivalent to registering the linking function via the
* `link` property of the config object when the compile function is empty.
*
* * returning an object with function(s) registered via `pre` and `post` properties - allows you to
* control when a linking function should be called during the linking phase. See info about
* pre-linking and post-linking functions below.
*
*
* #### `link`
* This property is used only if the `compile` property is not defined.
*
* <pre>
* function link(scope, iElement, iAttrs, controller) { ... }
* </pre>
*
* The link function is responsible for registering DOM listeners as well as updating the DOM. It is
* executed after the template has been cloned. This is where most of the directive logic will be
* put.
*
* * `scope` - {@link api/ng.$rootScope.Scope Scope} - The scope to be used by the
* directive for registering {@link api/ng.$rootScope.Scope#methods_$watch watches}.
*
* * `iElement` - instance element - The element where the directive is to be used. It is safe to
* manipulate the children of the element only in `postLink` function since the children have
* already been linked.
*
* * `iAttrs` - instance attributes - Normalized list of attributes declared on this element shared
* between all directive linking functions.
*
* * `controller` - a controller instance - A controller instance if at least one directive on the
* element defines a controller. The controller is shared among all the directives, which allows
* the directives to use the controllers as a communication channel.
*
*
*
* #### Pre-linking function
*
* Executed before the child elements are linked. Not safe to do DOM transformation since the
* compiler linking function will fail to locate the correct elements for linking.
*
* #### Post-linking function
*
* Executed after the child elements are linked. It is safe to do DOM transformation in the post-linking function.
*
* <a name="Attributes"></a>
* ### Attributes
*
* The {@link api/ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the
* `link()` or `compile()` functions. It has a variety of uses.
*
* accessing *Normalized attribute names:*
* Directives like 'ngBind' can be expressed in many ways: 'ng:bind', `data-ng-bind`, or 'x-ng-bind'.
* the attributes object allows for normalized access to
* the attributes.
*
* * *Directive inter-communication:* All directives share the same instance of the attributes
* object which allows the directives to use the attributes object as inter directive
* communication.
*
* * *Supports interpolation:* Interpolation attributes are assigned to the attribute object
* allowing other directives to read the interpolated value.
*
* * *Observing interpolated attributes:* Use `$observe` to observe the value changes of attributes
* that contain interpolation (e.g. `src="{{bar}}"`). Not only is this very efficient but it's also
* the only way to easily get the actual value because during the linking phase the interpolation
* hasn't been evaluated yet and so the value is at this time set to `undefined`.
*
* <pre>
* function linkingFn(scope, elm, attrs, ctrl) {
* // get the attribute value
* console.log(attrs.ngModel);
*
* // change the attribute
* attrs.$set('ngModel', 'new value');
*
* // observe changes to interpolated attribute
* attrs.$observe('ngModel', function(value) {
* console.log('ngModel has changed value to ' + value);
* });
* }
* </pre>
*
* Below is an example using `$compileProvider`.
*
* <div class="alert alert-warning">
* **Note**: Typically directives are registered with `module.directive`. The example below is
* to illustrate how `$compile` works.
* </div>
*
<doc:example module="compile">
<doc:source>
<script>
// declare a new module, and inject the $compileProvider
angular.module('compile', [], function($compileProvider) {
// configure new 'compile' directive by passing a directive
// factory function. The factory function injects the '$compile'