Working through the directive spectrum
Directives can be incorporated into HTML in several different ways. Depending on how this incorporation is done, the way the directive will interact with the DOM will change.
How to do it…
All directives are able to define a link
function, which defines how that particular directive instance will interact with the part of the DOM it is attached to. The link
functions have three parameters by default: the directive scope (which you will learn more about later), the relevant DOM element, and the element's attributes as key-value pairs.
A directive can exist in a template in four different ways: as an HTML pseudo-element, as an HTML element attribute, as a class, and as a comment.
The element directive
The element directive takes the form of an HTML tag. As with any HTML tag, it can wrap content, have attributes, and live inside other HTML elements.
The directive can be used in a template in the following fashion:
(index.html) <div ng-app="myApp"> <element-directive some-attr="myvalue"> <!-- directive's HTML contents --> </element-directive> </div>
This will result in the directive template replacing the wrapped contents of the <element-directive>
tag with the template. This element directive can be defined as follows:
(app.js) angular.module('myApp', []) .directive('elementDirective', function ($log) { return { restrict: 'E', template: '<p>Ze template!</p>', link: function(scope, el, attrs) { $log.log(el.html()); // <p>Ze template!</p> $log.log(attrs.someAttr); // myvalue } }; });
Tip
JSFiddle: http://jsfiddle.net/msfrisbie/sajhgjat/
Note that for both the tag string and the attribute string, AngularJS will match the CamelCase for elementDirective
and someAttr
to their hyphenated element-directive
and some-attr
counterparts in the markup.
If you want to replace the directive
tag entirely with the content instead, the directive will be defined as follows:
(index.html) angular.module('myApp', []) .directive('elementDirective', function ($log) { return { restrict: 'E', replace: true, template: '<p>Ze template!</p>', link: function(scope, el, attrs) { $log.log(el.html()); // Ze template! $log.log(attrs.someAttr); // myvalue } }; });
Tip
JSFiddle: http://jsfiddle.net/msfrisbie/oLhrm194/
This approach will operate in an identical fashion, but the directive's inner HTML will not be wrapped with <element-directive>
tags in the compiled HTML. Also, note that the logged template is missing its <p></p>
tags that have become the root directive element as they are the top-level tags inside the template.
The attribute directive
Attribute directives are the most commonly used form of directives, and for good reason. They have the following advantages:
- They can be added to existing HTML as standalone attributes, which is especially convenient if the directive's purpose doesn't require you to break up an existing template into fragments
- It is possible to add an unlimited amount of attribute directives to an HTML element, which is obviously not possible with an element directive
- Attribute directives attached to the same HTML element are able to communicate with each other (refer to the Interaction between nested directives recipe)
This directive can be used in a template in the following fashion:
(index.html) <div ng-app="myApp"> <div attribute-directive="aval" some-attr="myvalue"> </div> </div>
Tip
A nonstandard element's attributes need the data-
prefix to be compliant with the HTML5 specification. That being said, pretty much every modern browser will have no problem if you leave it out.
The attribute directive can be defined as follows:
(app.js) angular.module('myApp', []) .directive('attributeDirective', function ($log) { return { // restrict defaults to A restrict: 'A', template: '<p>An attribute directive</p>', link: function(scope, el, attrs) { $log.log(el.html()); // <p>An attribute directive</p> $log.log(attrs.attributeDirective); // aval $log.log(attrs.someAttr); // myvalue } }; });
Tip
JSFiddle: http://jsfiddle.net/msfrisbie/y2tsgxjt/
Other than its form in the HTML template, the attribute directive functions in pretty much the same way as an element directive. It assumes its attribute values from the container element's attributes, including the attribute directive and other directives (whether or not they are assigned a value).
The class directive
Class directives are not altogether that different from attribute directives. They provide the ability to have multiple directive assignments, unrestricted local attribute value access, and local directive communication.
This directive can be used in a template in the following fashion:
(index.html) <div ng-app="myApp"> <div class="class-directive: cval; normal-class" some-attr="myvalue"> </div> </div>
This attribute directive can be defined as follows:
(app.js) angular.module('myApp', []) .directive('classDirective', function ($log) { return { restrict: 'C', template: '<p>A class directive</p>', link: function(scope, el, attrs) { $log.log(el.html()); // <p>A class directive</p> $log.log(el.hasClass('normal-class')); // true $log.log(attrs.classDirective); // cval $log.log(attrs.someAttr); // myvalue } }; });
Tip
JSFiddle: http://jsfiddle.net/msfrisbie/rt1f4qxx/
It's possible to reuse class directives and assign CSS styling to them, as AngularJS leaves them alone when compiling the directive. Additionally, a value can be directly applied to the directive class name attribute by passing it in the CSS string.
The comment directive
Comment directives are the runt of the group. You will very infrequently find their use necessary, but it's useful to know that they are available in your application.
This directive can be used in a template in the following fashion:
(index.html) <div ng-app="myApp"> <!-- directive: comment-directive val1 val2 val3 --> </div>
The comment directive can be defined as follows:
(app.js) angular.module('myApp', []) .directive('commentDirective', function ($log) { return { restrict: 'M', // without replace: true, the template cannot // be inserted into the DOM replace: true, template: '<p>A comment directive</p>', link: function(scope, el, attrs) { $log.log(el.html()) // <p>A comment directive</p> $log.log(attrs.commentDirective) // 'val1 val2 val3' } }; });
Tip
JSFiddle: http://jsfiddle.net/msfrisbie/thfvx275/
Formerly, the primary use of comment directives was to handle scenarios where the DOM API made it difficult to create directives with multiple siblings. Since the release of AngularJS 1.2 and the inclusion of ng-repeat-start
and ng-repeat-end
, comment directives are considered an inferior solution to this problem, and therefore, they have largely been relegated to obscurity. Nevertheless, they can still be employed effectively.
How it works…
AngularJS actively compiles the template, searching for matches to defined directives. It's possible to chain directive forms together within the same definition. The mydir
directive with restrict: 'EACM'
can appear as follows:
<mydir></mydir> <div mydir></div> <div class="mydir"></dir> <!-- directive: mydir -->
There's more…
The $log.log()
statements in this recipe should have given you some insight into the extraordinary use that directives can have in your application.
See also
- The Interaction between nested directives recipe demonstrates how to allow directives attached to the same element to communicate with each other