Skip to main content
Ben Nadel at cf.Objective() 2014 (Bloomington, MN) with: Jared Rypka-Hauer
Ben Nadel at cf.Objective() 2014 (Bloomington, MN) with: Jared Rypka-Hauer

Breaking Changes In Isolate-Scope Behavior In AngularJS 1.2

By
Published in Comments (4)

With the release of AngularJS 1.2, there was a fairly large breaking change in the way that Isolate-scope directives interacted with child elements in the DOM (Document Object Model). And, while this may not matter to most people using AngularJS, I still run a lot of production code through AngularJS 1.0.8, so this is an important point for my team. In AngularJS 1.0.8 (and earlier), child elements [may] receive the isolate scope even when they are not part of the isolate directive.

Run this demo in my JavaScript Demos project on GitHub.

There's a growing movement, it seems, in the AngularJS community that you should "isolate all the things!" And, while I understand the sentiment, applying that philosophy in an AngularJS 1.0.8 application can actually break the application. The reason for this is that you will unwittingly tie child-element scope references into the isolate scope. Before AngularJS 1.2, the isolate scope "leaks" into the contextual-descendant DOM branch when you are not using transclusion.

To see this in action, I've put together a super simple demo that uses a Controller and an isolate-scope directive and attempts to interpolate a message.

Here is the version running AngularJS 1.0.8:

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

	<title>
		Breaking Changes In Isolate-Scope Behavior In AngularJS 1.2
	</title>
</head>
<body ng-controller="AppController">

	<h1>
		Breaking Changes In Isolate-Scope Behavior In AngularJS 1.2
	</h1>

	<!--
		Both the AppController and the bnIsolate directive define a scope
		reference for "message". In AngularJS 1.0.8, the message will be taken
		from the isolate scope.
	-->
	<p bn-isolate>
		<strong>AngularJS 1.0.8</strong>: {{ message }}
	</p>


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

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


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


		// I control the root of the application.
		app.controller(
			"AppController",
			function( $scope ) {

				$scope.message = "Hello from App Controller.";

			}
		);


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


		// I control the an isolate scope directive.
		app.directive(
			"bnIsolate",
			function() {

				// I bind the JavaScript events to the local scope.
				function link( scope, element, attributes ) {

					scope.message = "Hello from Isolate Scope.";

				}


				// Return the directive configuration. Notice that we are creating an
				// isolate scope, but not binding any values or expressions.
				return({
					link: link,
					restrict: "A",
					scope: {}
				});

			}
		);

	</script>

</body>
</html>

As you can see, the "{{ message }}" is inside the isolate-scope, lexically, but not in the isolate scope template. When we run this page, we get the following page output:

AngularJS 1.0.8: Hello from Isolate Scope.

As you can see, the main page interpolation is pulling the {{ message }} from the isolate scope even though it is not part of the isolate scope directive.

Now, here is the exact same demo in which the only thing we've changed is the version of AngularJS that we are running (1.2.26):

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

	<title>
		Breaking Changes In Isolate-Scope Behavior In AngularJS 1.2
	</title>
</head>
<body ng-controller="AppController">

	<h1>
		Breaking Changes In Isolate-Scope Behavior In AngularJS 1.2
	</h1>

	<!--
		Both the AppController and the bnIsolate directive define a scope
		reference for "message". In AngularJS 1.2.26, the message will be taken
		from the AppController since the child elements don't get the isolate scope
		unless they are part of the isolate scope template.
	-->
	<p bn-isolate>
		<strong>AngularJS 1.2.26</strong>: {{ message }}
	</p>


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

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


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


		// I control the root of the application.
		app.controller(
			"AppController",
			function( $scope ) {

				$scope.message = "Hello from App Controller.";

			}
		);


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


		// I control the an isolate scope directive.
		app.directive(
			"bnIsolate",
			function() {

				// I bind the JavaScript events to the local scope.
				function link( scope, element, attributes ) {

					scope.message = "Hello from Isolate Scope.";

				}


				// Return the directive configuration. Notice that we are creating an
				// isolate scope, but not binding any values or expressions.
				return({
					link: link,
					restrict: "A",
					scope: {}
				});

			}
		);

	</script>

</body>
</html>

Now, when we run this page, we get the following output:

AngularJS 1.2.26: Hello from App Controller.

As you can see, the main page interpolation is [correctly] pulling from the AppController, which is more likely to be what the developer intended.

There are things you can do to help each version of AngularJS act like the other (see the vide). But, for the most part, this should make you think twice about using isolate-scope directives in AngularJS 1.0.8. I'm not saying don't use them - just don't use them haphazardly.

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

Reader Comments

26 Comments

<blockquote>There's a growing movement, it seems, in the AngularJS community that you should "isolate all the things!"</blockquote>

I adopted this mentality as well, but unfortunately, you quickly run into problems when applying two directives, each with their own isolate scope, to the same DOM element...

15,902 Comments

@Vincent,

Ah, good point! A single element can only have one normal scope and one isolate scope, from what I recall. But, do the two isolate directives *share* the same isolate scope? Or does it throw an error? I can't remember off-hand.

26 Comments

@Ben,

It throws an error... So although I feel like I'd rather making pretty much every directive have an isolate scope, I am very reluctant to do so :(

15,902 Comments

@Vincent,

Yeah, especially if two isolate-scopes on the same directive will raise an error. That's good to know, thanks for checking!

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