Binding A Directive To Multiple Compilation And Linking Functions In AngularJS
A month ago, I demonstrated that a single directive, in AngularJS, could be bound to multiple priorities on the same element. This has a lot of value; but, at the time, I failed to step back and think about the mechanics of what was actually happening. It turns out, this behavior is bigger than multi-priority directives. Upon further investigation, it seems that a single directive "name" can be defined over and over again, providing multiple compilation and linking functions.
Run this demo in my JavaScript Demos project on GitHub.
To see this in action, I've defined a single directive - bnEmphasize - three times, providing three unique compilation and linking functions:
<!doctype html>
<html ng-app="Demo">
<head>
<meta charset="utf-8" />
<title>
Binding A Directive To Multiple Compilation And Linking Functions In AngularJS
</title>
</head>
<body>
<h1>
Binding A Directive To Multiple Compilation And Linking Functions In AngularJS
</h1>
<p bn-emphasize>
Hello world.
</p>
<p bn-emphasize>
I hope all is well.
</p>
<!-- Load scripts. -->
<script type="text/javascript" src="../../vendor/jquery/jquery-2.1.0.min.js"></script>
<script type="text/javascript" src="../../vendor/angularjs/angular-1.3.6.min.js"></script>
<script type="text/javascript">
// Create an application module for our demo.
var app = angular.module( "Demo", [] );
// -------------------------------------------------- //
// -------------------------------------------------- //
// I replace all periods with exclamation points. People need to know I'm
// serious about the things that I'm saying!
app.directive(
"bnEmphasize",
function() {
// Return the directive configuration.
return({
compile: function( tElement, tAttributes ) {
tElement.html( tElement.html().replace( /\./g, "!" ) );
// Return the linking function.
return(
function link( scope, element, attributes ) {
console.log( "Linking 1 for scope", scope.$id );
}
);
},
restrict: "A"
});
}
);
// I wrap the content in a Strong tag. How are people going to know that I mean
// bid'ness unless the text is extra dark!
app.directive(
"bnEmphasize",
function() {
// Return the directive configuration.
return({
compile: function( tElement, tAttributes ) {
// CAUTION: You cannot use .wrapInner() unless you include jQuery.
// You don't get this kind of sweet-ass functionality with jQLite.
tElement.wrapInner( "<strong></strong>" );
// Return the linking function.
return(
function link( scope, element, attributes ) {
console.log( "Linking 2 for scope", scope.$id );
}
);
},
restrict: "A"
});
}
);
// Bro! Bro! Nothing says "emphasis" like a Marquee. People love marquees.
// I wrap the content in a marquee for supreme classiness!
app.directive(
"bnEmphasize",
function() {
// Return the directive configuration.
return({
compile: function( tElement, tAttributes ) {
// CAUTION: You cannot use .wrapInner() unless you include jQuery.
// You don't get this kind of sweet-ass functionality with jQLite.
tElement.wrapInner( "<marquee></marquee>" );
// Return the linking function.
return(
function link( scope, element, attributes ) {
console.log( "Linking 3 for scope", scope.$id );
}
);
},
restrict: "A"
});
}
);
</script>
</body>
</html>
As you can see, one adds exclamation marks; one adds the Strong tag; and, one adds the Marquee tag. When we run this page, we get the following output:
As you can see, each bnEmphasize configuration acted on the same element. Furthermore, since the Marquee tag wraps the Strong tag, we can see that the directives were applied in the same order in which they were defined.
The value of this has already been explained, in part, in my post on multi-priority directives. But, now that I see how the mechanics of it actually work, I can see additional use-cases, especially for Element directives like "Script."
Want to use code from this post? Check out the license.
Reader Comments
Awesome stuff! Have to try it
Angular JS run the last directive then other before (Descending), but what's a raison to duplicate the some directive, because we can concatenate all that in one directive ?!!
@Kamal,
Great question, if I understand what you're asking. This can be useful if you need a directive to link at two different priorities. But, more than that, this can also be useful if you need to differentiate between two different directives during the compilation process.
Consider the "Script" tag. In AngularJS, the Script tag is an Element Directive. But, it only works for certain script tags - "text/ng-template". If we want to create another directive that works on different types of Script tags, it allows us to define the same name - Script - but give it its own compilation and linking phase.
I'll try to follow up with another post to that effect.
@Juri,
Thanks my man! Glad you like :D
@Kamal,
As a follow-up, I put together a demo that defines a new Script tag directive, in addition to the core Script tag directive that AngularJS already provides:
www.bennadel.com/blog/2745-creating-custom-script-tag-directives-in-angularjs.htm
Hopefully having two Scrip tag directives, that differentiate on TYPE, will help clarify why this is such a powerful feature.
So what happens here if we create an isolate scope on two different implementations of the same directive? Are they sharing the same scope then? Or does angular explode?
If we create multiple implementations of the same directive, what are properties that we want to avoid? I'm assuming, for things like `template` or `templateUrl`, only one will end up being used and that might be the one with the highest priority?
@Atticus,
It's funny timing for your question! I just had one of my staging environments "blow up" because someone fixed a merge-conflicts in Git in such a way that *accidentally* included the same directive definition twice. The directive happened to be using an Isolate scope AND the application broke (since you can't have multiple isolate scopes on the same element):
www.bennadel.com/blog/2747-accidentally-defining-a-directive-twice-in-angularjs.htm
That said, in my example (just linked), if the directive doesn't require an isolate scope, it won't "break", per say; but, it will not function properly (in so much as the directive gets linked twice).