AngularJS ng-repeat Directive with Example

⚡ Smart Summary

The ng-repeat directive in AngularJS renders one copy of an element for every item in a collection defined on the controller scope. It turns an array into a list, a table, or any repeating markup without writing a manual loop.

  • 🔁 Core Behaviour: ng-repeat clones its host element once per item, so the directive belongs on the element being repeated, not its container.
  • 🏷️ Loop Variable: The name before the word in becomes a local alias for the current item inside that clone.
  • 🧬 Child Scope: Every iteration receives its own child scope, which is why assigning a primitive inside the loop does not reach the parent.
  • 🔑 Identity Tracking: A track by expression tells AngularJS how to identify items, and it is required when a collection holds repeated values.
  • 🔢 Built-in Locals: Properties such as $index, $first, and $last are available inside each iteration for numbering and styling.
  • ⚠️ Support Status: AngularJS reached end of life on 31 December 2021 and receives no further security patches.

AngularJS ng-repeat Directive

ng-repeat Directive in AngularJS

The ng-repeat directive in AngularJS is used to display repeating values defined in the controller. Sometimes we need to show a list of items in the view, and ng-repeat renders a list defined in the controller onto a view page.

⚠️ Support notice: AngularJS reached end of life on 31 December 2021 and receives no further patches or security fixes. The directive below remains accurate for maintaining existing AngularJS 1.x applications; in modern Angular the equivalent is the *ngFor structural directive.

AngularJS ng-repeat Directive Example

Let us look at an example of the ng-repeat directive in AngularJS:

ng-repeat Directive in AngularJS

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Event Registration</title>
</head>
<body>
<h1>Guru99 Global Event</h1>
<script src="https://code.angularjs.org/1.8.3/angular.js"></script>

<div ng-app="DemoApp" ng-controller="DemoController">
    <h1>Topics</h1>
    <ul>
        <li ng-repeat="tpname in TopicNames">
            {{tpname.name}}
        </li>
    </ul>
</div>

<script>
    var app = angular.module('DemoApp', []);

    app.controller('DemoController', function($scope) {
        $scope.TopicNames = [
            { name: "What controllers do from Angular's perspective" },
            { name: "Controller Methods" },
            { name: "Building a basic controller" }
        ];
    });
</script>

</body>
</html>

Code Explanation:

  1. In the controller, we first define the array of list items to be shown in the view. Here an array called TopicNames holds three items, each a name-value pair.
  2. The array is assigned directly to $scope.TopicNames, which makes it available to the view bound to this controller.
  3. The HTML tags <ul> (unordered list) and <li> (list item) display the items. The ng-repeat directive iterates over each entry in the array, and tpname is the local variable holding the current item, so {{tpname.name}} prints its name property.

⚠️ Correction: the original explanation stated that the array was “added to a member variable called topics“. No such variable exists in the code — the array is assigned straight to $scope.TopicNames, which is the name the view refers to. Introducing a second name here is a common cause of an empty list.

If the code is executed successfully, the following output is shown in the browser.

ng-repeat Directive in AngularJS

AngularJS Multiple Controllers

Earlier we saw a single controller in which one method handled both addition and subtraction of numbers. You can instead use multiple controllers to separate logic more cleanly. For example, one controller can operate on numbers while another operates on strings.

Let us look at an example of defining multiple controllers in an AngularJS application.

AngularJS Multiple Controllers

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Event Registration</title>
</head>
<body>
<h1>Guru99 Global Event</h1>
<script src="https://code.angularjs.org/1.8.3/angular.js"></script>

<div ng-app="DemoApp">
    <div ng-controller="firstcontroller">
        <div ng-controller="secondcontroller">
            {{lname}}
        </div>
    </div>
</div>

<script>
    var app = angular.module('DemoApp', []);

    app.controller('firstcontroller', function($scope) {
        $scope.pname = "firstcontroller";
    });

    app.controller('secondcontroller', function($scope) {
        $scope.lname = "secondcontroller";
    });
</script>

</body>
</html>

Code Explanation:

  1. Two controllers are defined, firstcontroller and secondcontroller. Each attaches a variable to its own scope: pname in the first, lname in the second.
  2. In the view, the second controller is nested inside the first, and the expression reads lname from the inner scope. Because a nested controller inherits from its parent, {{pname}} would also resolve here — inheritance flows downward, never upward.

If the code is executed successfully, the text secondcontroller is displayed.

AngularJS Multiple Controllers

How to Use track by with ng-repeat

The single most common ng-repeat failure is the error Duplicates in a repeater are not allowed. It appears the moment a collection contains the same value twice, and it stops the list rendering entirely.

The cause is identity tracking. AngularJS needs a stable key for each item so that it can reuse DOM nodes rather than rebuild them on every digest. By default it uses the item value itself, so two identical values collide.

<!-- Fails: the value 2 appears twice -->
<li ng-repeat="n in [1, 2, 2, 3]">{{n}}</li>

<!-- Works: position is unique even when values repeat -->
<li ng-repeat="n in [1, 2, 2, 3] track by $index">{{n}}</li>

<!-- Best for records from a server: track by a real identifier -->
<li ng-repeat="topic in TopicNames track by topic.id">{{topic.name}}</li>

Choosing between the two forms matters more than it first appears. Tracking by $index always resolves the error, because a position is unique by definition, but it ties each DOM node to a slot rather than to a record. Reordering or filtering the collection then reuses the wrong nodes, which shows up as input fields keeping the previous row’s text.

Tracking by a genuine identifier such as topic.id avoids that entirely. AngularJS can follow each record as it moves, so it reorders existing nodes instead of destroying and rebuilding them. On long lists this is also markedly faster, since a re-render touches only the rows that actually changed. Use $index only for arrays of plain values that never reorder.

Special Properties Available Inside ng-repeat

Each iteration of ng-repeat exposes several read-only properties on its own child scope. They describe where the current item sits within the collection, which covers numbering, alternating styles, and separators without adding any extra state to the controller.

Property Type Value
$index Number Position of the current item, starting at 0
$first Boolean True for the first item only
$last Boolean True for the last item only
$middle Boolean True for every item that is neither first nor last
$even Boolean True when $index is even
$odd Boolean True when $index is odd

A typical use combines them with ng-class for zebra striping, as in ng-class="{'row-alt': $odd}", or with ng-if to render a separator on every row except the last, written ng-if="!$last". Displaying a human-readable row number is simply {{$index + 1}}, since the index is zero-based. All six properties are recalculated automatically whenever the collection changes, so nothing needs to be recomputed by hand.

Common ng-repeat Errors and How to Fix Them

Most ng-repeat problems come from duplicate values, from scope inheritance, or from the directive sitting on the wrong element. Each symptom below names its cause.

  • Duplicates in a repeater are not allowed: the collection holds a repeated value. Add track by $index, or better, track by a unique property.
  • The list renders nothing: the name in the view does not match the property on the scope. Print the collection with {{TopicNames}} to confirm it arrived.
  • Every element repeats, including the wrapper: ng-repeat was placed on the container rather than on the item. Move it to the <li> or <tr>.
  • Editing an input changes the wrong row: the list is tracked by $index while items reorder. Track by a stable identifier instead.
  • Changes inside the loop do not persist: each iteration has its own child scope, so writing to a primitive creates a local copy. Write to an object property, such as item.done.

FAQs

Yes, using ng-repeat="(key, value) in myObject". Note that AngularJS iterates object keys in sorted order, not insertion order, so an array is safer when sequence matters.

Chain filters in the expression, as in ng-repeat="t in TopicNames | filter:search | orderBy:'name'". The original array is untouched; only the rendered order and subset change.

Yes. AI tools map ng-repeat to *ngFor and track by to trackBy functions. Check filters carefully, because Angular deliberately dropped the built-in filter and orderBy pipes.

AI tools spot function calls inside the repeat expression, which re-run on every digest, and missing track by clauses that force full DOM rebuilds. Both are the usual causes of a sluggish list.

Use the comment form, ng-repeat-start and ng-repeat-end, which repeats a range of sibling elements. This matters in tables, where an extra div would break the markup.

Summarize this post with: