Skip to main content
Ben Nadel at cf.Objective() 2017 (Washington, D.C.) with: Brad Wood
Ben Nadel at cf.Objective() 2017 (Washington, D.C.) with: Brad Wood

Changes In Transclude() Function Availability In AngularJS 1.2

By
Published in Comments (4)

The other day, my friend Sam Lin pointed out that my use of the transclude() function in a JavaScript demo had been deprecated in AngularJS 1.2. After looking at the documentation, I was quite pleased to find out that he was right. While the change appears fairly cosmetic, I think it makes the directive configuration much more straightforward. Now, rather than having to define the compile() function just to get a reference to transclude(), the transclue() function is passed directly into the link() function.

View this demo in my JavaScript-Demos project on GitHub.

Before AngularJS 1.2, if you wanted a directive to transclude its element, you had to define the compile() function so that you could get a reference to the transclude() function. This was because the compile() function was the only place the transclude() function was made available (outside of explicit calls to $compile). As such, you ended up with a number of directives that had a non-functional - but required - compile() wrapper.

With AngularJS 1.2, however, this superfluos compile wrapper is no longer necessary; if your directive configuration defines transclusion, the transclusion function is now passed directly into the link() function as the fifth (5th) argument, after the optional controller(s) argument.

To see this in action, I created a simple demo that uses both the old [deprecated] and the new approaches to transclude() function injection:

<!doctype html>
<html ng-app="Demo">
<head>
	<meta charset="utf-8" />

	<title>
		Transclude Function Passed To Link Function In AngularJS 1.2
	</title>
</head>
<body>

	<h1>
		Transclude Function Passed To Link Function In AngularJS 1.2
	</h1>

	<!-- Will demonstrate old-school transclude configuration. -->
	<p bn-old-transclude>
		Old Transclude!
	</p>

	<!-- Will demonstrate new, easier transclude configuration. -->
	<p bn-new-transclude>
		New Transclude!
	</p>


	<!-- Load scripts. -->
	<script type="text/javascript" src="../../vendor/jquery/jquery-2.0.3.min.js"></script>
	<script type="text/javascript" src="../../vendor/angularjs/angular-1.2.4.min.js"></script>
	<script type="text/javascript">


		// Create an application module for our demo.
		var app = angular.module( "Demo", [] );


		// -------------------------------------------------- //
		// -------------------------------------------------- //


		// In the old-school transclude configuration, you NEEDED to
		// define the compile() method just to get a reference to the
		// transclude function. Notice that the compile() isn't
		// actually doing anything - it's just there so that the
		// "transclude" argument can be defined.
		app.directive(
			"bnOldTransclude",
			function() {

				function compile( tElement, tAttributes, transclude ) {

					// At this point, the "element" is the COMMENT
					// that has been injected into the DOM as an anchor
					// for the subsequent transclusion.
					function link( $scope, element, attributes ) {

						transclude(
							$scope,
							function( clone ) {

								clone.css( "border", "2px solid pink" );

								element.after( clone );

							}
						);

					}

					return( link );

				}


				// Return the directive configuration.
				return({
					compile: compile,
					restrict: "A",
					transclude: "element"
				});

			}
		);


		// -------------------------------------------------- //
		// -------------------------------------------------- //


		// In the new transclude configuration, you no longer need the
		// COMPILE function _just_ to define the transclude function;
		// the transclude function is now passed as an optional
		// argument directly to the link() function.
		// --
		// NOTE: I defined the "controller" argument since the
		// transclude function is after it. However, since I am not
		// actually "require"ing any controllers, I am naming it "null."
		app.directive(
			"bnNewTransclude",
			function() {

				// At this point, the "element" is the COMMENT that
				// has been injected into the DOM as an anchor for
				// the subsequent transclusion.
				function link( $scope, element, attributes, nullController, transclude ) {

					transclude(
						$scope,
						function( clone ) {

							clone.css( "border", "2px solid gold" );

							element.after( clone );

						}
					);

				}


				// Return the directive configuration.
				return({
					link: link,
					restrict: "A",
					transclude: "element"
				});

			}
		);


	</script>

</body>
</html>

Both of the above directives do the exact same thing; only, the second one doesn't use or require the compile() function in order to access the transclude() function. This is a fairly cosmetic change - but it is certainly a much cleaner and much more convenient approach. I'm really glad they went in this direction.

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

Reader Comments

50 Comments

With Angular it seems more powerful but less mature as a platform. One of the struggles CFML has is there are not as many solutions as richly supported by developers as WordPress. We have some CMS solutions but not with as many plugin apps or skin themes.

Likewise it reminds me of the early days of jQuery when there were very few plugins. It made coding easier but it is clear things like jQuery UI have been big game changers. Even companies like Telerik with KendoUI and similar solutions are proof of concept to having a robust "full featured" starting point wins for many.

I was looking at the AngularJS UI library that works with Bootstrap. They are working on version 3 bootstrap compatibility but it's a work in progress.

So here is my question. When building a customer site the need to update things often means new code updates of core libraries. How would you rate the maturity of AngularJS in terms of sustainable code issues?

15,848 Comments

@John,

All good questions. I was just listening to a podcast where the guest was talking about AngularJS vs. EmberJS. One of the things he said that he liked about Ember was that it was very opinionated; that if you went from one EmberJS team to another, you would be able to get around the app because all EmberJS apps work in similar ways (this is what I remember he said - but not working with EmberJS, I cannot verify).

AngularJS, on the other hand, is a lot more open-ended; and, an AngularJS app built by one team may work in a completely different way than an AngularJS app built by another team.

So, this difference in philosophy likely has its own set of pros / cons.

As far as code changes, we are about to upgrade from AngularJS 1.0.2 to 1.0.8. This jump only has a few minor breaking changes that are mostly tied to some CSS tweaks and some poorly written code (meaning, bad code that didn't cause a problem before will start to cause a problem now).

That said, trying to go from 1.0.8 to 1.2 would be a HUGE jump for our app and requiring a significant amount of code-rework. The big jump in 1.2 revolves around animations which seemed to required a different DOM structure (which is where our approach gets screwed over a bit).

The DOM fixes are easy; but, they would be so wide spread in the app that it would be a non-trivial change.

At the end of the day, AngularJS still sits on top of jQuery (or JQLite if you don't include the jQuery library); so, anything you can do with jQuery you can do with AngularJS. It's just that AngularJS has a strict definition as to where certain types of code should go.

All in all, I really do like it a lot!

1 Comments

Another thing worth pointing out is around the order of compile / link phases of parent directive and transcluded content. Normally Angular compiles way down and links way up. But the fact that transclude function is called in the link function of the parent directive implies that you get a chance to call the parent's link function prior to calling the link function of the transcluded content.

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