Skip to main content
Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.

Sanity Check: $index vs. DOM In AngularJS Directives

By Ben Nadel on

With AngularJS, there is such a strong separation between the DOM (Document Object Model) and your Controller / Model / Service components, that I sometimes find myself unsure as to when I can depend on the existence of dynamically-generated DOM nodes within AngularJS directives. The other day, I had a moment of anxiety about the $index value added by the ngRepeat directive; and, I found myself having to run a sanity check on how the DOM lines up with the $index values.

Run this demo in my JavaScript Demos project on GitHub.

In an ngRepeat loop, AngularJS automatically adds an $index value (among others) to the item-based $scope that gets created in the loop. In this sanity check, I wanted to see if watching for changes in the $index value would accurately reflect changes in the current DOM structure. In this case, I'm logging the number of generated LI elements whenever the $index changes.

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

		Sanity Check: $index vs. DOM In AngularJS Directives

	<style type="text/css">

		a[ ng-click ] {
			cursor: pointer ;
			text-decoration: underline ;

<body ng-controller="AppController">

		Sanity Check: $index vs. DOM In AngularJS Directives

	<form ng-submit="addFriend()">

		<input type="text" ng-model="" />
		<input type="submit" value="Add Friend" />


			ng-repeat="friend in friends"

				As each ngRepeat item is rendered, we're going to
				listen for changes on the auto-generated $index value.
			<span bn-index-watch>{{ }}</span>

			( <a ng-click="removeFriend( friend )">Remove</a> )


	<!-- 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.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.
			function( $scope ) {

				// I am the collection of friends being rendered.
				$scope.friends = [
						name: "Sarah"

				// I am the form-input collection for ngModel.
				$scope.form = {
					name: ""

				// ---
				// ---

				// I add a new friend, using the form input.
				$scope.addFriend = function() {

						name: $

					$ = "";


				// I remove the given friend from the collection.
				$scope.removeFriend = function( friend ) {

						$scope.friends.indexOf( friend ),



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

		// I watch for changes in the $index and compare it to the DOM.
			function() {

				// I wire the $scope to the DOM.
				function link( $scope, element, attributes ) {

						function( newValue, oldValue ) {

							// Collection the rendered DOM elments and
							// the index in the context of the current
							// directive / DOM element.
							// --
							// NOTE: We are making the index 1-based
							// for an easier-to-read comparison.
							var friendCount = $( "li.friend" ).length;
							var indexCount = ( newValue + 1 );

							console.log( indexCount, ",", friendCount );



				// Return directive configuration.
					link: link,
					restrict: "A"




After adding three friends to the collection, I get the following console log output:

1 , 1
2 , 2
3 , 3
4 , 4

As I added items, you can see that the changes in the $index value match the changes in the DOM.

After removing three friends from the top of the list (in order to actually force $index changes on existing items), I get the following console log output:

1 , 3
2 , 3
3 , 3
1 , 2
2 , 2
1 , 1

As I deleted items, you can see that the changes in the $index value match the changes in the DOM.

Sanity check accomplished! The $index value is an accurate way to gauge the state of the Document Object Model (DOM) in the context of an AngularJS ngRepeat directive.

Reader Comments

I had NOOOO idea you could pass the entire friend object into a method like this: removeFriend( friend ) .

I was always using some ID of the object and passing that around back and forth between views/controllers/services.

Checking this out tonight.

Neat and thanks.

I got here for the same reason as @John Allen, i.e. - get the index for deleting a an entry from an array ViewModel.

Thanks for the showing the easy way to do that Ben.