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.

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:
<!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:
- In the controller, we first define the array of list items to be shown in the view. Here an array called
TopicNamesholds three items, each a name-value pair. - The array is assigned directly to
$scope.TopicNames, which makes it available to the view bound to this controller. - The HTML tags
<ul>(unordered list) and<li>(list item) display the items. The ng-repeat directive iterates over each entry in the array, andtpnameis 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.
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.
<!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:
- Two controllers are defined,
firstcontrollerandsecondcontroller. Each attaches a variable to its own scope:pnamein the first,lnamein the second. - In the view, the second controller is nested inside the first, and the expression reads
lnamefrom 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.
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 bya 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
$indexwhile 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.




