257

Angular does provide some support for a for loop using numbers within its HTML directives:

<div data-ng-repeat="i in [1,2,3,4,5]">
  do something
</div>

But if your scope variable includes a range that has a dynamic number then you will need to create an empty array each time.

In the controller

var range = [];
for(var i=0;i<total;i++) {
  range.push(i);
}
$scope.range = range;

In the HTML

<div data-ng-repeat="i in range">
  do something
</div>

This works, but it is unnecessary since we won't be using the range array at all within the loop. Does anyone know of setting a range or a regular for min/max value?

Something like:

<div data-ng-repeat="i in 1 .. 100">
  do something
</div>
domino_katrino
  • 356
  • 3
  • 19
matsko
  • 21,895
  • 21
  • 102
  • 144
  • 3
    Here's some more info about this. http://www.yearofmoo.com/2012/10/more-angularjs-magic-to-supercharge-your-webapp.html#more-about-loops – matsko Oct 26 '12 at 15:35

24 Answers24

288

I tweaked this answer a bit and came up with this fiddle.

Filter defined as:

var myApp = angular.module('myApp', []);
myApp.filter('range', function() {
  return function(input, total) {
    total = parseInt(total);

    for (var i=0; i<total; i++) {
      input.push(i);
    }

    return input;
  };
});

With the repeat used like this:

<div ng-repeat="n in [] | range:100">
  do something
</div>
Community
  • 1
  • 1
Gloopy
  • 37,767
  • 15
  • 103
  • 71
151

I came up with an even simpler version, for creating a range between two defined numbers, eg. 5 to 15

See demo on JSFiddle

HTML:

<ul>
    <li ng-repeat="n in range(5,15)">Number {{n}}</li>
</ul>

Controller:

$scope.range = function(min, max, step) {
    step = step || 1;
    var input = [];
    for (var i = min; i <= max; i += step) {
        input.push(i);
    }
    return input;
};
sqren
  • 22,833
  • 7
  • 52
  • 36
  • 16
    You have to watch out for things like this. The range function will get called multiple times for each item in the list. You can get nasty memory leaks and you're generating a lot of function calls. It almost seems easier to put the collection inside the controller. – Lucas Holt Jun 18 '13 at 16:33
  • 1
    `var a; void 0 === a` "truly" undefined. – Andreas Louv Aug 19 '14 at 11:05
  • 1
    You can reduce the perf hit by caching the results via the memoizer pattern. Not sure what the memory cost is, but it is an improvement. http://en.wikipedia.org/wiki/Memoization. – Joshua Feb 04 '15 at 07:56
  • 1
    @LucasHolt Finally got around to updating this example. I've added an optimized version. It will still have many function calls, but it will re-use the first result which makes it much more performant. – sqren Nov 13 '15 at 21:14
101

Nothing but plain Javascript (you don't even need a controller):

<div ng-repeat="n in [].constructor(10) track by $index">
    {{ $index }}
</div>

Very useful when mockuping

Maël Nison
  • 7,055
  • 7
  • 46
  • 77
  • Not working in Angular >1.2.1: I've got this error in the console `Referencing "constructor" field in Angular expressions is disallowed! Expression: [].constructor(10)`. Maybe that was working in a previous version of Angular or I'm doing something wrong. – AWolf Apr 03 '15 at 22:20
  • I think I tested this on 1.3 or 1.4 and it was fine. I believe they have improved the parser so it doesn't reject every 'constructor' access but only the really dangerous ones (such as `[].slice.constructor`, which gives access to `Function`, hence to code evaluation). – Maël Nison Apr 04 '15 at 09:04
  • OK, thanks. I tested it with 1.2.1 and it was not working (the version included in jsFiddle dropdown). But with 1.3.15 it is working. See this [jsFiddle](http://jsfiddle.net/awolf2904/r5411e11/). – AWolf Apr 04 '15 at 11:37
  • Greatly useful for pagination. Thanks ;) – Stranger Mar 29 '16 at 15:40
70

I came up with a slightly different syntax which suits me a little bit more and adds an optional lower bound as well:

myApp.filter('makeRange', function() {
        return function(input) {
            var lowBound, highBound;
            switch (input.length) {
            case 1:
                lowBound = 0;
                highBound = parseInt(input[0]) - 1;
                break;
            case 2:
                lowBound = parseInt(input[0]);
                highBound = parseInt(input[1]);
                break;
            default:
                return input;
            }
            var result = [];
            for (var i = lowBound; i <= highBound; i++)
                result.push(i);
            return result;
        };
    });

which you can use as

<div ng-repeat="n in [10] | makeRange">Do something 0..9: {{n}}</div>

or

<div ng-repeat="n in [20, 29] | makeRange">Do something 20..29: {{n}}</div>
Mormegil
  • 7,955
  • 4
  • 42
  • 77
  • This is great, but how do you get around all the `$digest` iterations when using a scoped value, ie: `ng-repeat="n in [1, maxValue] | makeRange"`? – pspahn Jun 30 '15 at 22:03
  • The one and two param cases treat highBound differently, it should be consistent – Vajk Hermecz Sep 29 '16 at 11:40
31

For those new to angularjs. The index can be gotten by using $index.

For example:

<div ng-repeat="n in [] | range:10">
    do something number {{$index}}
</div>

Which will, when you're using Gloopy's handy filter, print:
do something number 0
do something number 1
do something number 2
do something number 3
do something number 4
do something number 5
do something number 6
do something number 7
do something number 8
do something number 9

Arnoud Sietsema
  • 558
  • 5
  • 11
  • 7
    True, but in this case `do something number {{n}}` would also suffice. – captncraig Nov 28 '13 at 05:11
  • 2
    When I try to run this code in Chrome I get an error in the console:Error: [$injector:unpr] http://errors.angularjs.org/1.2.6/$injector/unpr?p0=rangeFilterProvider%20%3C-%20rangeFilter ... ...at Wa.statements (https://ajax.googleapis.com/ajax/libs/angularjs/1.2.6/angular.min.js:156:273) (Unfortunately the entire error is too long to fit in allowed comment length so I just copied the first and last line) – Kmeixner Dec 30 '13 at 21:11
  • 1
    I've created a Plunker example of the code so you can compare it to your code. http://plnkr.co/edit/tN0156hRfX0M6k9peQ2C?p=preview – Arnoud Sietsema Jan 04 '14 at 17:39
21

A short way of doing this would be to use Underscore.js's _.range() method. :)

http://underscorejs.org/#range

// declare in your controller or wrap _.range in a function that returns a dynamic range.
var range = _.range(1, 11);

// val will be each number in the array not the index.
<div ng-repeat='val in range'>
    {{ $index }}: {{ val }}
</div>
Michael J. Calkins
  • 32,082
  • 15
  • 62
  • 91
  • 1
    @jwoodward He never said that wasn't an option. My solution is by far the simplest and most tested thanks to unit testing from their repo. You can always take the js out of the unminified library and include it as a function. – Michael J. Calkins Nov 22 '13 at 01:48
  • 4
    True, he never said that. But it is generally a bad idea to solve a problem in one library by applying another library, it just prevents you from learning how use the first library properly. Turn to another library when/if there is no good solution in the current one. – Erik Honn Dec 04 '13 at 13:50
  • 5
    I already use lodash everywhere, this one was by far the easiest solution. Thank you. I just did $scope.range = _.range, then its easy to use in any template with dynamic start/end values. – j_walker_dev May 08 '14 at 09:52
  • 2
    @ErikHonn: UnderscoreJS solves a completely different problem to AngularJS. There is no good solution for generating ranges in AngularJS because that is not the purpose of the library. – Alex Jun 24 '14 at 14:31
  • Except there is a solution for it, you write a custom directive or a very simple filter. This is why you should avoid solving problems by applying another library, it prevents you from learning. That is not to say other libraries can never be used, but you should always try to solve it without first. Of course, if you already have underscore for other reasons, go ahead, but it is still good to learn the real way :P – Erik Honn Jun 24 '14 at 15:07
  • 2
    This is old, but surely using a library designed to do things exactly like creating a range, instead of abusing a feature in your current library in a non-semantic way is a far better way to go? Sure you may learn something, but you end up with distorted usages of things designed for something else. – Dan May 08 '16 at 20:09
21

I use my custom ng-repeat-range directive:

/**
 * Ng-Repeat implementation working with number ranges.
 *
 * @author Umed Khudoiberdiev
 */
angular.module('commonsMain').directive('ngRepeatRange', ['$compile', function ($compile) {
    return {
        replace: true,
        scope: { from: '=', to: '=', step: '=' },

        link: function (scope, element, attrs) {

            // returns an array with the range of numbers
            // you can use _.range instead if you use underscore
            function range(from, to, step) {
                var array = [];
                while (from + step <= to)
                    array[array.length] = from += step;

                return array;
            }

            // prepare range options
            var from = scope.from || 0;
            var step = scope.step || 1;
            var to   = scope.to || attrs.ngRepeatRange;

            // get range of numbers, convert to the string and add ng-repeat
            var rangeString = range(from, to + 1, step).join(',');
            angular.element(element).attr('ng-repeat', 'n in [' + rangeString + ']');
            angular.element(element).removeAttr('ng-repeat-range');

            $compile(element)(scope);
        }
    };
}]);

and html code is

<div ng-repeat-range from="0" to="20" step="5">
    Hello 4 times!
</div>

or simply

<div ng-repeat-range from="5" to="10">
    Hello 5 times!
</div>

or even simply

<div ng-repeat-range to="3">
    Hello 3 times!
</div>

or just

<div ng-repeat-range="7">
    Hello 7 times!
</div>
pleerock
  • 18,322
  • 16
  • 103
  • 128
9

Simplest no code solution was to init an array with the range, and use the $index + however much I want to offset by:

<select ng-init="(_Array = []).length = 5;">
    <option ng-repeat="i in _Array track by $index">{{$index+5}}</option>
</select>
Vince
  • 757
  • 1
  • 8
  • 16
8

Method definition

The code below defines a method range() available to the entire scope of your application MyApp. Its behaviour is very similar to the Python range() method.

angular.module('MyApp').run(['$rootScope', function($rootScope) {
    $rootScope.range = function(min, max, step) {
        // parameters validation for method overloading
        if (max == undefined) {
            max = min;
            min = 0;
        }
        step = Math.abs(step) || 1;
        if (min > max) {
            step = -step;
        }
        // building the array
        var output = [];
        for (var value=min; value<max; value+=step) {
            output.push(value);
        }
        // returning the generated array
        return output;
    };
}]);

Usage

With one parameter:

<span ng-repeat="i in range(3)">{{ i }}, </span>

0, 1, 2,

With two parameters:

<span ng-repeat="i in range(1, 5)">{{ i }}, </span>

1, 2, 3, 4,

With three parameters:

<span ng-repeat="i in range(-2, .7, .5)">{{ i }}, </span>

-2, -1.5, -1, -0.5, 0, 0.5,

bluray
  • 1,875
  • 5
  • 36
  • 68
Mathieu Rodic
  • 6,637
  • 2
  • 43
  • 49
8

Without any change in your controller, you can use this:

ng-repeat="_ in ((_ = []) && (_.length=51) && _) track by $index"

Enjoy!

odroz
  • 151
  • 1
  • 6
6

You can use 'after' or 'before' filters in angular.filter module (https://github.com/a8m/angular-filter)

$scope.list = [1,2,3,4,5,6,7,8,9,10]

HTML:

<li ng-repeat="i in list | after:4">
  {{ i }}
</li>

result: 5, 6, 7, 8, 9, 10

a8m
  • 9,334
  • 4
  • 37
  • 40
3

Shortest answer: 2 lines of code

JS (in your AngularJS controller)

$scope.range = new Array(MAX_REPEATS); // MAX_REPEATS should be the most repetitions you will ever need in a single ng-repeat

HTML

<div data-ng-repeat="i in range.slice(0,myCount) track by $index"></div>

...where myCount is the number of stars that should appear in this location.

You can use $index for any tracking operations. E.g. if you want to print some mutation on the index, you might put the following in the div:

{{ ($index + 1) * 0.5 }}
JellicleCat
  • 28,480
  • 24
  • 109
  • 162
3

Very simple one:

$scope.totalPages = new Array(10);

 <div id="pagination">
    <a ng-repeat="i in totalPages track by $index">
      {{$index+1}}
    </a>   
 </div> 
Evan Hu
  • 977
  • 1
  • 13
  • 18
3

Hi you can achieve this using pure html using AngularJS (NO Directive is required!)

<div ng-app="myapp" ng-controller="YourCtrl" ng-init="x=[5];">
  <div ng-if="i>0" ng-repeat="i in x">
    <!-- this content will repeat for 5 times. -->
    <table class="table table-striped">
      <tr ng-repeat="person in people">
         <td>{{ person.first + ' ' + person.last }}</td>
      </tr>
    </table>
    <p ng-init="x.push(i-1)"></p>
  </div>
</div>
Pavan Kosanam
  • 79
  • 3
  • 13
2

Using UnderscoreJS:

angular.module('myModule')
    .run(['$rootScope', function($rootScope) { $rootScope.range = _.range; }]);

Applying this to $rootScope makes it available everywhere:

<div ng-repeat="x in range(1,10)">
    {{x}}
</div>
Alex
  • 7,639
  • 3
  • 45
  • 58
2
<div ng-init="avatars = [{id : 0}]; flag = true ">
  <div ng-repeat='data in avatars' ng-if="avatars.length < 10 || flag"
       ng-init="avatars.length != 10 ? avatars.push({id : $index+1}) : ''; flag = avatars.length <= 10 ? true : false">
    <img ng-src="http://actual-names.com/wp-content/uploads/2016/01/sanskrit-baby-girl-names-400x275.jpg">
  </div>
</div>

If you want to achieve this in html without any controller or factory.

1

Set Scope in controller

var range = [];
for(var i=20;i<=70;i++) {
  range.push(i);
}
$scope.driverAges = range;

Set Repeat in Html Template File

<select type="text" class="form-control" name="driver_age" id="driver_age">
     <option ng-repeat="age in driverAges" value="{{age}}">{{age}}</option>
</select>
Ahmer
  • 94
  • 5
1

An improvement to @Mormegil's solution

app.filter('makeRange', function() {
  return function(inp) {
    var range = [+inp[1] && +inp[0] || 0, +inp[1] || +inp[0]];
    var min = Math.min(range[0], range[1]);
    var max = Math.max(range[0], range[1]);
    var result = [];
    for (var i = min; i <= max; i++) result.push(i);
    if (range[0] > range[1]) result.reverse();
    return result;
  };
});

usage

<span ng-repeat="n in [3, -3] | makeRange" ng-bind="n"></span>

3 2 1 0 -1 -2 -3

<span ng-repeat="n in [-3, 3] | makeRange" ng-bind="n"></span>

-3 -2 -1 0 1 2 3

<span ng-repeat="n in [3] | makeRange" ng-bind="n"></span>

0 1 2 3

<span ng-repeat="n in [-3] | makeRange" ng-bind="n"></span>

0 -1 -2 -3

mildog8
  • 2,030
  • 2
  • 22
  • 36
1

I tried the following and it worked just fine for me:

<md-radio-button ng-repeat="position in formInput.arrayOfChoices.slice(0,6)" value="{{position}}">{{position}}</md-radio-button>

Angular 1.3.6

araghorn
  • 41
  • 3
1

Late to the party. But i ended up just doing this:

In your controller:

$scope.repeater = function (range) {
    var arr = []; 
    for (var i = 0; i < range; i++) {
        arr.push(i);
    }
    return arr;
}

Html:

<select ng-model="myRange">
    <option>3</option>
    <option>5</option>
</select>

<div ng-repeat="i in repeater(myRange)"></div>
mnsr
  • 12,337
  • 4
  • 53
  • 79
1

This is jzm's improved answer (i cannot comment else i would comment her/his answer because s/he included errors). The function has a start/end range value, so it's more flexible, and... it works. This particular case is for day of month:

$scope.rangeCreator = function (minVal, maxVal) {
    var arr = [];
   for (var i = minVal; i <= maxVal; i++) {
      arr.push(i);
   }
   return arr;
};


<div class="col-sm-1">
    <select ng-model="monthDays">
        <option ng-repeat="day in rangeCreator(1,31)">{{day}}</option>
    </select>
</div>
fresko
  • 1,890
  • 2
  • 24
  • 25
0

I whipped this up and saw it might be useful for some. (Yes, CoffeeScript. Sue me.)

Directive

app.directive 'times', ->
  link: (scope, element, attrs) ->
    repeater = element.html()
    scope.$watch attrs.times, (value) ->
      element.html ''
      return unless value?
      element.html Array(value + 1).join(repeater)

To use:

HTML

<div times="customer.conversations_count">
  <i class="icon-picture></i>
</div>

Can this get any simpler?

I'm wary about filters because Angular likes to re-evaluate them for no good reason all the time, and it's a huge bottleneck if you have thousands of them like I do.

This directive will even watch for changes in your model, and update the element accordingly.

Prathan Thananart
  • 4,007
  • 3
  • 19
  • 18
  • This is nice, but ngRepeat maintains it's own scoping. So you'll have to run compile for each item that's added. You'll also need to emulate animations since ngRepeat does that for you. – matsko Jun 27 '13 at 03:43
  • 2
    Thanks for contributing, but assuming the OP knows coffeescript, what it is, how to compile it back to JS, or has ever used any build tools like that is a huge stretch. We're all here to learn or help, so, go the extra mile, convert that puppy. (You've been sued!) – Augie Gardner Jul 11 '14 at 19:24
0

Suppose $scope.refernceurl is an array then

for(var i=0; i<$scope.refernceurl.length; i++){
    $scope.urls+=$scope.refernceurl[i].link+",";
}
THess
  • 1,003
  • 1
  • 13
  • 21
-8

This is the simplest variant: just use array of integers....

 <li ng-repeat="n in [1,2,3,4,5]">test {{n}}</li>
Stefan
  • 15
  • 3