ChatGPT解决这个技术问题 Extra ChatGPT

AngularJS For Loop with Numbers & Ranges

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>
Here's some more info about this. yearofmoo.com/2012/10/…

C
Community

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>

Interesting I don't see an error in the fiddle using Chrome. Which browser?
Horrid! ;) A filter isn't the way to do this. It should be a new attribute that works with two values. The current index and the upper bound.
@IanWarburton Are you able to come up with an answer which meets your criteria?
@IanWarburton: There is an Answer by Уmed below that works with a directive, thou i can't get it to work.
I needed a range between A and B so i tweeked this answer jsfiddle.net/h9nLc8mk
s
sqren

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;
};

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.
var a; void 0 === a "truly" undefined.
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. en.wikipedia.org/wiki/Memoization.
@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.
M
Maël Nison

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


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.
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).
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.
Greatly useful for pagination. Thanks ;)
M
Mormegil

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>

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"?
The one and two param cases treat highBound differently, it should be consistent
A
Arnoud Sietsema

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


True, but in this case do something number {{n}} would also suffice.
When I try to run this code in Chrome I get an error in the console:Error: [$injector:unpr] errors.angularjs.org/1.2.6/$injector/… ... ...at Wa.statements (ajax.googleapis.com/ajax/libs/angularjs/1.2.6/…) <!-- ngRepeat: n in [] | range:10 --> (Unfortunately the entire error is too long to fit in allowed comment length so I just copied the first and last line)
I've created a Plunker example of the code so you can compare it to your code. plnkr.co/edit/tN0156hRfX0M6k9peQ2C?p=preview
M
Michael J. Calkins

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>

@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.
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.
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.
@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.
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.
p
pleerock

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>

Theoretically nice, but the injection of element.attr('ng-repeat', 'n in [' + rangeString + ']'); does not work ...
"I use...". Have you actually tried to use it? The ng-repeat attribute will not be compiled.
Thanks Peter, Ive updated the code. I used this directive in past, but it still left my code repository
V
Vince

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>

b
bluray

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,


a
a8m

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


o
odroz

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

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

Enjoy!


have been struggling more than a day and finally only this syntax worked for me
J
JellicleCat

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 }}

P
Pavan Kosanam

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>

A
Alex

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>

E
Evan Hu

Very simple one:

$scope.totalPages = new Array(10);

 <div id="pagination">
    <a ng-repeat="i in totalPages track by $index">
      {{$index+1}}
    </a>   
 </div> 

A
Ahmer

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>

t
theliberalsurfer

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


a
araghorn

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


m
mnsr

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>

f
fresko

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>

V
Virendra Singh Rathore
<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.


P
Prathan Thananart

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.


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.
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!)
T
THess

Suppose $scope.refernceurl is an array then

for(var i=0; i<$scope.refernceurl.length; i++){
    $scope.urls+=$scope.refernceurl[i].link+",";
}

S
Stefan

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

  • test {{n}}

  • This is horrible.
    @Stefan, you're likely receiving downvotes because the solution cannot use hard-coded collections.