Compiles an HTML string or DOM into a template and produces a template function, which can then be used to link scope
and the template together.
The compilation is a process of walking the DOM tree and matching DOM elements to directives.
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).
Here's an example directive declared with a Directive Definition Object:
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) { ... }, transclude: false, restrict: 'A', templateNamespace: 'html', scope: false, controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... }, controllerAs: 'stringIdentifier', bindToController: false, require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'], multiElement: false, 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; });
Therefore the above can be simplified as:
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) { ... } });
Directive controllers can provide the following methods that are called by Angular at points in the life-cycle of the directive:
$onInit()
- Called on each controller after all the controllers on an element have been constructed and had their bindings initialized (and before the pre & post linking functions for the directives on this element). This is a good place to put initialization code for your controller.$onChanges(changesObj)
- Called whenever one-way (<
) or interpolation (@
) bindings are updated. The changesObj
is a hash whose keys are the names of the bound properties that have changed, and the values are an object of the form { currentValue, previousValue, isFirstChange() }
. Use this hook to trigger updates within a component such as cloning the bound value to prevent accidental mutation of the outer value.$doCheck()
- Called on each turn of the digest cycle. Provides an opportunity to detect and act on changes. Any actions that you wish to take in response to the changes that you detect must be invoked from this hook; implementing this has no effect on when $onChanges
is called. For example, this hook could be useful if you wish to perform a deep equality check, or to check a Date object, changes to which would not be detected by Angular's change detector and thus not trigger $onChanges
. This hook is invoked with no arguments; if detecting changes, you must store the previous value(s) for comparison to the current values.$onDestroy()
- Called on a controller when its containing scope is destroyed. Use this hook for releasing external resources, watches and event handlers. Note that components have their $onDestroy()
hooks called in the same order as the $scope.$broadcast
events are triggered, which is top down. This means that parent components will have their $onDestroy()
hook called before child components.$postLink()
- Called after this controller's element and its children have been linked. Similar to the post-link function this hook can be used to set up DOM event handlers and do direct DOM manipulation. Note that child elements that contain templateUrl
directives will not have been compiled and linked since they are waiting for their template to load asynchronously and their own compilation and linking has been suspended until that occurs.Angular 2 also uses life-cycle hooks for its components. While the Angular 1 life-cycle hooks are similar there are some differences that you should be aware of, especially when it comes to moving your code from Angular 1 to Angular 2:
$
, such as $onInit
. Angular 2 hooks are prefixed with ng
, such as ngOnInit
.$doCheck
in Angular 1 than you would to ngDoCheck
in Angular 2$doCheck
will trigger new turns of the digest loop, which will cause the changes to be propagated throughout the application. Angular 2 does not allow the ngDoCheck
hook to trigger a change outside of the component. It will either throw an error or do nothing depending upon the state of enableProdMode()
.This example shows how you can check for mutations to a Date object even though the identity of the object has not changed.
This example show how you might use $doCheck
to trigger changes in your component's inputs even if the actual identity of the component doesn't change. (Be aware that cloning and deep equality checks on large arrays or objects can have a negative impact on your application performance)
The directive definition object provides instructions to the compiler. The attributes are:
When this property is set to true (default is false
), the HTML compiler will collect DOM nodes between nodes with the attributes directive-name-start
and directive-name-end
, and group them together as the directive elements. It is recommended that this feature be used on directives which are not strictly behavioral (such as ngClick
), and which do not manipulate or replace child nodes (such as ngInclude
).
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. Pre-link functions are also run in priority order, but post-link functions are run in reverse order. The order of directives with the same priority is undefined. The default priority is 0
.
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). Note that expressions and other directives used in the directive's template will also be excluded from execution.
The scope property can be false
, true
, or an object:
false
(default): No scope will be created for the directive. The directive will use its parent's scope.
true
: A new child scope that prototypically inherits from its parent will be created for the directive's element. If multiple directives on the same element request a new scope, only one new scope is created.
{...}
(an object hash): A new "isolate" scope is created for the directive's element. The 'isolate' scope differs from normal scope in that it does not prototypically inherit from its parent scope. This is useful when creating reusable components, which should not accidentally read or modify data in the parent scope.
The 'isolate' scope object hash defines a set of local scope properties derived from attributes on the directive's element. These local properties are useful for aliasing values for templates. The keys in the object hash map to the name of the property on the isolate scope; the values define how the property is bound to the parent scope, via matching attributes on the directive's element:
@
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 <my-component
my-attr="hello {{name}}">
and the isolate scope definition scope: { localName:'@myAttr' }
, the directive's scope property localName
will reflect the interpolated value of hello
{{name}}
. As the name
attribute changes so will the localName
property on the directive's scope. The name
is read from the parent scope (not the directive's scope).
=
or =attr
- set up a bidirectional binding between a local scope property and an expression passed via the attribute attr
. The expression is evaluated 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 <my-component my-attr="parentModel">
and the isolate scope definition scope: {
localModel: '=myAttr' }
, the property localModel
on the directive's scope will reflect the value of parentModel
on the parent scope. Changes to parentModel
will be reflected in localModel
and vice versa. Optional attributes should be marked as such with a question mark: =?
or =?attr
. If the binding expression is non-assignable, or if the attribute isn't optional and doesn't exist, an exception ($compile:nonassign
) will be thrown upon discovering changes to the local value, since it will be impossible to sync them back to the parent scope. By default, the $watch
method is used for tracking changes, and the equality check is based on object identity. However, if an object literal or an array literal is passed as the binding expression, the equality check is done by value (using the angular.equals
function). It's also possible to watch the evaluated value shallowly with $watchCollection
: use =*
or =*attr
(=*?
or =*?attr
if the attribute is optional).
<
or <attr
- set up a one-way (one-directional) binding between a local scope property and an expression passed via the attribute attr
. The expression is evaluated 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. You can also make the binding optional by adding ?
: <?
or <?attr
.
For example, given <my-component my-attr="parentModel">
and directive definition of scope: { localModel:'<myAttr' }
, then the isolated scope property localModel
will reflect the value of parentModel
on the parent scope. Any changes to parentModel
will be reflected in localModel
, but changes in localModel
will not reflect in parentModel
. There are however two caveats:
$watch
on the parent value only fires if the reference to the value has changed. In most cases, this should not be of concern, but can be important to know if you one-way bind to an object, and then replace that object in the isolated scope. If you now change a property of the object in your parent scope, the change will not be propagated to the isolated scope, because the identity of the object on the parent scope has not changed. Instead you must assign a new object.One-way binding is useful if you do not plan to propagate changes to your isolated scope bindings back to the parent. However, it does not make this completely impossible.
&
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 <my-component my-attr="count = count + value">
and the isolate scope definition scope: {
localFn:'&myAttr' }
, the 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 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})
.
In general it's possible to apply more than one directive to one element, but there might be limitations depending on the type of scope required by the directives. The following points will help explain these limitations. For simplicity only two directives are taken into account, but it is also applicable for several directives:
This property is used to bind scope properties directly to the controller. It can be either true
or an object hash with the same format as the scope
property.
When an isolate scope is used for a directive (see above), bindToController: true
will allow a component to have its properties bound to the controller, rather than to scope.
After the controller is instantiated, the initial values of the isolate scope bindings will be bound to the controller properties. You can access these bindings once they have been initialized by providing a controller method called $onInit
, which is called after all the controllers on an element have been constructed and had their bindings initialized.
this
before the controller constructor is called, this use is now deprecated. Please place initialization code that relies upon bindings inside a $onInit
method on the controller, instead. It is also possible to set bindToController
to an object hash with the same format as the scope
property. This will set up the scope bindings to the controller directly. Note that scope
can still be used to define which kind of scope is created. By default, no scope is created. Use scope: {}
to create an isolate scope (useful for component directives).
If both bindToController
and scope
are defined and have object hashes, bindToController
overrides scope
.
Controller constructor function. The controller is instantiated before the pre-linking phase and can be accessed by 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([scope], cloneLinkingFn, futureParentElement, slotName)
:scope
: (optional) override the scope.cloneLinkingFn
: (optional) argument to create clones of the original transcluded content.futureParentElement
(optional):cloneLinkingFn
will add the cloned elements.$element.parent()
resp. $element
for transclude:'element'
resp. transclude:true
.cloneLinkingFn
is passed, as those elements need to created and cloned in a special way when they are defined outside their usual containers (e.g. like <svg>
).directive.templateNamespace
property.slotName
: (optional) the name of the slot to transclude. If falsy (e.g. null
, undefined
or ''
) then the default transclusion is provided. The $transclude
function also has a method on it, $transclude.isSlotFilled(slotName)
, which returns true
if the specified slot contains content (i.e. one or more DOM nodes).Require another directive and inject its controller as the fourth argument to the linking function. The require
property can be a string, an array or an object:
require
propertyIf the require
property is an object and bindToController
is truthy, then the required controllers are bound to the controller using the keys of the require
property. This binding occurs after all the controllers have been constructed but before $onInit
is called. If the name of the required controller is the same as the local name (the key), the name can be omitted. For example, {parentDir: '^^'}
is equivalent to {parentDir: '^^parentDir'}
. See the $compileProvider
helper for an example of how this can be used. If no such required directive(s) can be found, or if the directive does not have a controller, then an error is raised (unless no link function is specified and the required controllers are not being bound to the directive controller, in which case error checking is skipped). The name can be prefixed with:
?
- Attempt to locate the required controller or pass null
to the link
fn if not found.^
- Locate the required controller by searching the element and its parents. Throw an error 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 and its parents or pass null
to the link
fn if not found.?^^
- Attempt to locate the required controller by searching the element's parents, or pass null
to the link
fn if not found.Identifier name for a reference to the controller in the directive's scope. This allows the controller to be referenced from the directive template. This is especially useful when a directive is used as component, i.e. with an isolate
scope. It's also possible to use it in a directive without an isolate
/ new
scope, but you need to be aware that the controllerAs
reference might overwrite a property that already exists on the parent scope.
String of subset of EACM
which restricts the directive to a specific directive declaration style. If omitted, the defaults (elements and attributes) are used.
E
- Element name (default): <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 -->
String representing the document type used by the markup in the template. AngularJS needs this information as those elements need to be created and cloned in a special way when they are defined outside their usual containers like <svg>
and <math>
.
html
- All root nodes in the template are HTML. Root nodes may also be top-level elements such as <svg>
or <math>
.svg
- The root nodes in the template are SVG elements (excluding <math>
).math
- The root nodes in the template are MathML elements (excluding <svg>
).If no templateNamespace
is specified, then the namespace is considered to be html
.
HTML markup that may:
replace
is true - DEPRECATED).transclude
is true).Value may be:
<div red-on-hover>{{delete_str}}</div>
.tElement
and tAttrs
(described in the compile
function api below) and returns a string value.This is similar to template
but the template is loaded from the specified URL, asynchronously.
Because template loading is asynchronous the compiler will suspend compilation of directives on that element for later when the template has been resolved. In the meantime it will continue to compile and link sibling and parent elements as though this element had not contained any directives.
The compiler does not suspend the entire compilation to wait for templates to be loaded because this would result in the whole app "stalling" until all templates are loaded asynchronously - even in the case when only one deeply nested directive has templateUrl
.
Template loading is asynchronous even if the template has been preloaded into the $templateCache
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 $sce.getTrustedResourceUrl.
specify what the template should replace. Defaults to false
.
true
- the template will replace the directive's element.false
- the template will replace the contents of the directive's element.The replacement process migrates all of the attributes / classes from the old element to the new one. See the Directives Guide for an example.
There are very few scenarios where element replacement is required for the application function, the main one being reusable custom components that are used within SVG contexts (because SVG doesn't work with custom elements in the DOM tree).
Extract the contents of the element where the directive appears and make it available to the directive. The contents are compiled and provided to the directive as a transclusion function. See the Transclusion section below.
function compile(tElement, tAttrs, transclude) { ... }
The compile function deals with transforming the template DOM. Since most directives do not do template transformation, it is not used often. 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
- [DEPRECATED!] A transclude linking function: function(scope, cloneLinkingFn)
template
or templateUrl
declaration or manual compilation inside the compile function. transclude
function that is passed to the compile function is deprecated, as it e.g. does not know about the right outer scope. Please use the transclude function that is passed to the link function instead. 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.
This property is used only if the compile
property is not defined.
function link(scope, iElement, iAttrs, controller, transcludeFn) { ... }
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
- Scope - The scope to be used by the directive for registering 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
- the directive's required controller instance(s) - Instances are shared among all directives, which allows the directives to use the controllers as a communication channel. The exact value depends on the directive's require
property:
undefined
if it doesn't have onestring
: the controller instancearray
: array of controller instancesIf a required controller cannot be found, and it is optional, the instance is null
, otherwise the Missing Required Controller error is thrown.
Note that you can also require the directive's own controller - it will be made available like any other controller.
transcludeFn
- A transclude linking function pre-bound to the correct transclusion scope. This is the same as the $transclude
parameter of directive controllers, see the controller section for details. function([scope], cloneLinkingFn, futureParentElement)
.
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.
Executed after the child elements are linked.
Note that child elements that contain templateUrl
directives will not have been compiled and linked since they are waiting for their template to load asynchronously and their own compilation and linking has been suspended until that occurs.
It is safe to do DOM transformation in the post-linking function on elements that are not waiting for their async templates to be resolved.
Transclusion is the process of extracting a collection of DOM elements from one part of the DOM and copying them to another part of the DOM, while maintaining their connection to the original AngularJS scope from where they were taken.
Transclusion is used (often with ngTransclude
) to insert the original contents of a directive's element into a specified place in the template of the directive. The benefit of transclusion, over simply moving the DOM elements manually, is that the transcluded content has access to the properties on the scope from which it was taken, even if the directive has isolated scope. See the Directives Guide.
This makes it possible for the widget to have private state for its template, while the transcluded content has access to its originating scope.
There are three kinds of transclusion depending upon whether you want to transclude just the contents of the directive's element, the entire element or multiple parts of the element contents:
true
- transclude the content (i.e. the child nodes) of the directive's element.'element'
- transclude the whole of the directive's element including any directives on this element that defined at a lower priority than this directive. When used, the template
property is ignored.{...}
(an object hash): - map elements of the content onto transclusion "slots" in the template.Mult-slot transclusion is declared by providing an object for the transclude
property.
This object is a map where the keys are the name of the slot to fill and the value is an element selector used to match the HTML to the slot. The element selector should be in normalized form (e.g. myElement
) and will match the standard element variants (e.g. my-element
, my:element
, data-my-element
, etc).
For further information check out the guide on Matching Directives
If the element selector is prefixed with a ?
then that slot is optional.
For example, the transclude object { slotA: '?myCustomElement' }
maps <my-custom-element>
elements to the slotA
slot, which can be accessed via the $transclude
function or via the ngTransclude
directive.
Slots that are not marked as optional (?
) will trigger a compile time error if there are no matching elements in the transclude content. If you wish to know if an optional slot was filled with content, then you can call $transclude.isSlotFilled(slotName)
on the transclude function passed to the directive's link function and injectable into the directive's controller.
When a directive requests transclusion, the compiler extracts its contents and provides a transclusion function to the directive's link
function and controller
. This transclusion function is a special linking function that will return the compiled contents linked to a new transclusion scope.
ngTransclude
then you don't need to worry about this function, since ngTransclude will deal with it for us. If you want to manually control the insertion and removal of the transcluded content in your directive then you must use this transclude function. When you call a transclude function it returns a a jqLite/JQuery object that contains the compiled DOM, which is linked to the correct transclusion scope.
When you call a transclusion function you can pass in a clone attach function. This function accepts two parameters, function(clone, scope) { ... }
, where the clone
is a fresh compiled copy of your transcluded content and the scope
is the newly created transclusion scope, which the clone will be linked to.
cloneFn
(clone attach function) when you call a transclude function since you then get a fresh clone of the original DOM and also have access to the new transclusion scope. It is normal practice to attach your transcluded content (clone
) to the DOM inside your clone attach function:
var transcludedContent, transclusionScope; $transclude(function(clone, scope) { element.append(clone); transcludedContent = clone; transclusionScope = scope; });
Later, if you want to remove the transcluded content from your DOM then you should also destroy the associated transclusion scope:
transcludedContent.remove(); transclusionScope.$destroy();
element.remove()
to remove it), then you are also responsible for calling $destroy
on the transclusion scope. The built-in DOM manipulation directives, such as ngIf
, ngSwitch
and ngRepeat
automatically destroy their transcluded clones as necessary so you do not need to worry about this if you are simply using ngTransclude
to inject the transclusion into your directive.
When you call a transclude function it returns a DOM fragment that is pre-bound to a transclusion scope. This scope is special, in that it is a child of the directive's scope (and so gets destroyed when the directive's scope gets destroyed) but it inherits the properties of the scope from which it was taken.
For example consider a directive that uses transclusion and isolated scope. The DOM hierarchy might look like this:
<div ng-app> <div isolate> <div transclusion> </div> </div> </div>
The $parent
scope hierarchy will look like this:
- $rootScope - isolate - transclusion
but the scopes will inherit prototypically from different scopes to their $parent
.
- $rootScope - transclusion - isolate
The 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
.
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); }); }
module.directive
. The example below is to illustrate how $compile
works. Double compilation occurs when an already compiled part of the DOM gets compiled again. This is an undesired effect and can lead to misbehaving directives, performance issues, and memory leaks. Refer to the Compiler Guide section on double compilation for an in-depth explanation and ways to avoid it.
$compile(element, transclude, maxPriority);
Param | Type | Details |
---|---|---|
element | string DOMElement | Element or HTML string to compile into a template function. |
transclude | function(angular.Scope, cloneAttachFn=) | function available to directives - DEPRECATED. Note: Passing a transclude function to the $compile function is deprecated, as it e.g. will not use the right outer scope. Please pass the transclude function as a parentBoundTranscludeFn to the link function instead. |
maxPriority | number | only apply directives lower than given priority (Only effects the root element(s), not their children) |
function(scope, cloneAttachFn=, options=) |
a link function which is used to bind template (a DOM element/tree) to a scope. Where:
Calling the linking function returns the element of the template. It is either the original element passed in, or the clone of the element if the After linking the view is not updated until after a call to $digest which typically is done by Angular automatically. If you need access to the bound view, there are two ways to do it:
For information on how the compiler works, see the Angular HTML Compiler section of the Developer Guide. |
© 2010–2017 Google, Inc.
Licensed under the Creative Commons Attribution License 4.0.
https://code.angularjs.org/1.5.11/docs/api/ng/service/$compile