Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
Ben Nadel at cf.Objective() 2009 (Minneapolis, MN) with: Kurt Wiersma
Ben Nadel at cf.Objective() 2009 (Minneapolis, MN) with: Kurt Wiersma

Binding A Directive To Multiple Compilation And Linking Functions In AngularJS

By Ben Nadel on

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:


 
 
 

 
 Defining duplicate directives in AngularJS. 
 
 
 

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."




Reader Comments

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 ?!!

Reply to this Comment

@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.

Reply to this Comment

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?

Reply to this Comment

@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):

http://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).

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.