Skip to main content
Ben Nadel at cf.Objective() 2014 (Bloomington, MN) with: Jonathan Smith
Ben Nadel at cf.Objective() 2014 (Bloomington, MN) with: Jonathan Smith

Binding A Directive To Multiple Compilation And Linking Functions In AngularJS

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

Want to use code from this post? Check out the license.

Reader Comments

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

15,640 Comments

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

10 Comments

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?

15,640 Comments

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

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel