16

I have a JSON object that contains an int property x, I would like to repeat following code x times

<span class="glyphicon glyphicon-star"/>

ng-repeat does not seem to be indicated as it's working with collection.
Any suggestion (angular newbie)

Frederic Close
  • 9,389
  • 6
  • 56
  • 67
  • 3
    http://stackoverflow.com/questions/16824853/ng-repeat-defined-number-of-times-instead-of-repeating-over-array – Mik378 Dec 10 '13 at 22:10
  • 1
    You have hit one of the corner cases where `ng-repeat` doesn't quite fit the bill; you want something of the kind `ng-replicate="n"` but which doesn't exist in the angular bindings (yet). So the choices you have are either declaring an `Array(n)` in the controller and then looping over it or creating `ng-replicate` yourself. – musically_ut Dec 10 '13 at 22:11
  • Possible duplicate of [Iteration ng-repeat only X times in AngularJs](http://stackoverflow.com/questions/14198017/iteration-ng-repeat-only-x-times-in-angularjs) – T J Mar 04 '16 at 10:38

6 Answers6

23

I would use custom filter with ng-repeat:

HTML

<div ng-app='myApp' ng-controller="Main">
    <li ng-repeat="n in [] | range:20">
      <span class="glyphicon glyphicon-star" >{{n}}</span>
    </li>
</div>

filter

app.filter('range', function() {
  return function(val, range) {
    range = parseInt(range);
    for (var i=0; i<range; i++)
      val.push(i);
    return val;
  };
});

Demo Fiddle

Maxim Shoustin
  • 77,483
  • 27
  • 203
  • 225
8

Shortest answer: 2 lines of code

JS (in your AngularJS controller)

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

HTML

<span class="glyphicon glyphicon-star" ng-repeat="i in range.slice(0,starCount) track by $index"/>

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

JellicleCat
  • 28,480
  • 24
  • 109
  • 162
4

you can write filter range:

'use strict';

angular.module('app.Filter')
.filter('range', function() {
    return function(input, total) {
        total = parseInt(total);
        for (var i=0; i < total; ++i) {
            input.push(i);
        }
        return input;
    };
});

then use it

<span class="glyphicon glyphicon-star" ng-repeat="i in [] | range:5"/>

5 its your x

Vladimir Gordienko
  • 3,260
  • 3
  • 18
  • 25
4

When I was first starting to play with AngularJS, I found a decent tutorial that walks you through making a custom directive for doing a "Rating" widget in Angularjs.

http://www.befundoo.com/university/tutorials/angularjs-directives-tutorial/

They don't do anything special other than create a collection of empty objects based on the value of a two-way bound scope variable defined in the directive.

directive('fundooRating', function () {
    return {
        restrict: 'A',
        template: '<ul class="rating">' +
                      '<li ng-repeat="star in stars" class="filled">' +
                          '\u2605' +
                      '</li>' +
                  '</ul>',
        scope: {
            ratingValue: '='
        },
        link: function (scope, elem, attrs) {
            scope.stars = [];
            for (var i = 0; i < scope.ratingValue; i++) {
                scope.stars.push({});
            }
        }
    }
});

The nice thing is that the collection messiness is at least encapsulated inside of the directive, and all the controller has to do is deal with the the numerical rating value. The tutorial also walks you through doing two way linking between the directive and the controller on the rating value scope variable.

IMHO, this is the cleanest solution since AngularJS doesn't directly support what you want to do. At least here, it is easy to understand what you are trying to do in your controller and view (which is where you want to avoid unnecessary complexity) and you move the hackish-ness into the directive (which you will probably write once and forget).

reblace
  • 4,115
  • 16
  • 16
2

Angular (V1.2.9 and up) includes a filter limitTo:n that will do this out of the box. For example to limit ng-repeat to the first 20 elements use the following syntax:

<div ng-app='myApp' ng-controller="Main">
    <li ng-repeat="n in [] | limitTo:20">
      <span class="glyphicon glyphicon-star" >{{n}}</span>
    </li>
</div>

Documentation for limitTo is here

To be fair the limitTo filter didn't exist at the time the original question was asked.

RonnBlack
  • 3,998
  • 6
  • 26
  • 28
  • Wasn't able to make this work (angularjs 1.5.9), the best alternative I found was http://stackoverflow.com/a/14198102/1085483. My guess is that this does not work because limitTo returns an empty array with lenght 20, but ng-repeat does not iterate on empty arrays without using 'track by $index'. – Rui Marques Jan 19 '17 at 14:39
0

I answered a similar (duplicate?) question https://stackoverflow.com/a/31864879/1740079. I'll just repost here as I also ended up here searching for an answer.

(function () {
  angular
    .module('app')
    .directive('repeatTimes', repeatTimes);

  function repeatTimes ($window, $compile) {
    return { link: link };

    function link (scope, element, attrs) {
      var times    = scope.$eval(attrs.repeatTimes),
          template = element.clone().removeAttr('repeat-times');

      $window._(times).times(function (i) {
        var _scope = angular.extend(scope.$new(), { '$index': i });
        var html = $compile(template.clone())(_scope);

        html.insertBefore(element);
      });

      element.remove();
    }
  }
})();

... and the html:

<div repeat-times="4">{{ $index }}</div>

LIVE EXAMPLE

Community
  • 1
  • 1
ichigolas
  • 7,595
  • 27
  • 50