Skip to main content
Ben Nadel at CFUNITED 2008 (Washington, D.C.) with: Hal Helms
Ben Nadel at CFUNITED 2008 (Washington, D.C.) with: Hal Helms

Directive Templates Have A Small Impact On Performance In AngularJS

By
Published in Comments (1)

All developers want to keep their code DRY (Do not Repeat Yourself). But, as with all things computer science, DRYness can be a tradeoff, perhaps for performance. In AngularJS, directive templates allow code to be reused in multiple places; but, what impact does this have on rendering performance? After a little digging, it seems that directive templates do have a negative impact on rendering; but, that impact is very small.

Run this demo in my JavaScript Demos project on GitHub.

For this experiment, I'm going to render two different lists - friends and enemies - using ngRepeat. I use ngRepeat for these kinds of experiments because the ngRepeat directive is really where performance bottlenecks become symptomatic; your bindings don't really grow out-of-control until you start iterating over datasets.

In the first version of the code, the content of the ngRepeat directive will be duplicated inline. In the second version of the code, the content of the ngRepeat will be moved into a new directive - bnPerson - so we can remove the duplication.

First, the inline version:

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

	<title>
		Directive Templates Have A Small Impact On Performance In AngularJS
	</title>

	<link rel="stylesheet" type="text/css" href="./demo.css"></link>
</head>
<body ng-controller="AppController">

	<h1>
		Directive Templates Have A Small Impact On Performance In AngularJS
	</h1>

	<h2>
		Duplicating Inline Code
	</h2>

	<p>
		<a ng-click="toggleLists()">Toggle Lists</a>
	</p>

	<div
		ng-if="isShowingLists"
		ng-include=" 'list.htm' ">

		<!-- Content pulled-in as template to simulate real-world architecture. -->

	</div>


	<!-- I am the template used to render the main page. -->
	<script id="list.htm" type="text/ng-template">

		<div class="friends">

			<h2>
				Friends
			</h2>

			<ul>
				<li ng-repeat="friend in friends track by friend.id">

					{{ friend.id }} &mdash; {{ friend.name }}

				</li>
			</ul>

		</div>

		<div class="enemies">

			<h2>
				Enemies
			</h2>

			<ul>
				<li ng-repeat="enemy in enemies track by enemy.id">

					{{ enemy.id }} &mdash; {{ enemy.name }}

				</li>
			</ul>

		</div>

	</script>


	<!-- Load scripts. -->
	<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 control the root of the application.
		app.controller(
			"AppController",
			function( $scope ) {

				// I hold the lists being rendered.
				$scope.friends = buildList( "Sarah", 1000 );
				$scope.enemies = buildList( "Shane", 1000 );

				// I determine if the lists should be shown.
				$scope.isShowingLists = false;


				// ---
				// PUBLIC METHODS.
				// ---


				// I toggle the rendering of the lists (physically removing them from
				// the page if they should not be there).
				$scope.toggleLists = function() {

					$scope.isShowingLists = ! $scope.isShowingLists;

				};


				// ---
				// PRIVATE METHODS.
				// ---


				// I build a list of people using the given size.
				function buildList( name, count ) {

					var people = [];

					for ( var i = 1 ; i <= count ; i++ ) {

						people.push({
							id: i,
							name: name
						});

					}

					return( people );

				}

			}
		);

	</script>

</body>
</html>

As you can see, the content of the ngRepeat is inline in both lists:

{{ friend.id }} &mdash; {{ friend.name }}

And, while the ngRepeat item is different (friend vs. enemy), the content is basically the same. So much so that we can factor it out into a directive that renders the ngRepeat using a template.

In this version, we're going to add the bnPerson directive to the ngRepeat element. This directive will take a scope reference (friend vs. enemy) so that the content can be unified:

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

	<title>
		Directive Templates Have A Small Impact On Performance In AngularJS
	</title>

	<link rel="stylesheet" type="text/css" href="./demo.css"></link>
</head>
<body ng-controller="AppController">

	<h1>
		Directive Templates Have A Small Impact On Performance In AngularJS
	</h1>

	<h2>
		Using Directive Template To Reduce Duplication
	</h2>

	<p>
		<a ng-click="toggleLists()">Toggle Lists</a>
	</p>

	<div
		ng-if="isShowingLists"
		ng-include=" 'list.htm' ">

		<!-- Content pulled-in as template to simulate real-world architecture. -->

	</div>


	<!-- I am the template used to render the main page. -->
	<script id="list.htm" type="text/ng-template">

		<div class="friends">

			<h2>
				Friends
			</h2>

			<ul>
				<li
					ng-repeat="friend in friends track by friend.id"
					bn-person="friend">

					<!-- Content supplied by bnPerson directive template. -->

				</li>
			</ul>

		</div>

		<div class="enemies">

			<h2>
				Enemies
			</h2>

			<ul>
				<li
					ng-repeat="enemy in enemies track by enemy.id"
					bn-person="enemy">

					<!-- Content supplied by bnPerson directive template. -->

				</li>
			</ul>

		</div>

	</script>


	<!-- I am the template used to render the bnPerson directive. -->
	<script id="person.htm" type="text/ng-template">

		{{ person.id }} &mdash; {{ person.name }}

	</script>


	<!-- Load scripts. -->
	<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 control the root of the application.
		app.controller(
			"AppController",
			function( $scope ) {

				// I hold the lists being rendered.
				$scope.friends = buildList( "Sarah", 1000 );
				$scope.enemies = buildList( "Shane", 1000 );

				// I determine if the lists should be shown.
				$scope.isShowingLists = false;


				// ---
				// PUBLIC METHODS.
				// ---


				// I toggle the rendering of the lists (physically removing them from
				// the page if they should not be there).
				$scope.toggleLists = function() {

					$scope.isShowingLists = ! $scope.isShowingLists;

				};


				// ---
				// PRIVATE METHODS.
				// ---


				// I build a list of people using the given size.
				function buildList( name, count ) {

					var people = [];

					for ( var i = 1 ; i <= count ; i++ ) {

						people.push({
							id: i,
							name: name
						});

					}

					return( people );

				}

			}
		);


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


		// I provide the layout and behavior for a "Person".
		app.directive(
			"bnPerson",
			function() {

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

					// Watch for changes to the linked scope value and assign to person
					// as needed.
					// --
					// NOTE: This is what isolate-scope does behing the scenes to
					// implement two-way data binding... at least partially; only w're
					// doing it without having to alter the scope tree. The trade-off
					// is that we populate the "parent scope" with a new value.
					scope.$watch(
						attributes.bnPerson,
						function handlePersonBindingChangeEvent( newValue ) {

							scope.person = newValue;

						}
					);

				}


				// Return the isolate-scope directive configuration.
				return({
					link: link,
					templateUrl: "person.htm"
				});

			}
		);

	</script>

</body>
</html>

As you can see, the repetition has been factored out into a directive template. This keeps the code more DRY; but, it does have a small impact on performance (see the video above). That said, the impact is very small and should probably not be seen as a contraindication for directive usage.

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

Reader Comments

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