AngularJS para bucle con números y rangos

252

Angular proporciona cierto soporte para un bucle for utilizando números dentro de sus directivas HTML:

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

Pero si su variable de alcance incluye un rango que tiene un número dinámico, deberá crear una matriz vacía cada vez.

En el controlador

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

En el HTML

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

Esto funciona, pero es innecesario ya que no utilizaremos la matriz de rango en absoluto dentro del bucle. ¿Alguien sabe de establecer un rango o un valor regular para el valor mínimo / máximo?

Algo como:

<div data-ng-repeat="i in 1 .. 100">
  do something
</div>
Matsko
fuente
2
Aquí hay más información sobre esto. yearofmoo.com/2012/10/…
matsko

Respuestas:

281

Ajusté esta respuesta un poco y se me ocurrió este violín .

Filtro definido como:

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

Con la repetición utilizada así:

<div ng-repeat="n in [] | range:100">
  do something
</div>
Gloopy
fuente
Interesante, no veo un error en el violín usando Chrome. Cual navegador?
Gloopy
55
¡Horrible! ;) Un filtro no es la forma de hacer esto. Debe ser un nuevo atributo que funcione con dos valores. El índice actual y el límite superior.
Ian Warburton
10
@IanWarburton ¿Puede llegar a una respuesta que cumpla con sus criterios?
rocío
1
@IanWarburton: Hay una respuesta de Уmed a continuación que funciona con una directiva, no puedo hacer que funcione.
Andresch Serj
1
Necesitaba un rango entre A y B, así que modifiqué esta respuesta jsfiddle.net/h9nLc8mk
Marcio
150

Se me ocurrió una versión aún más simple, para crear un rango entre dos números definidos, por ejemplo. 5 a 15

Ver demostración en JSFiddle

HTML :

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

Controlador :

$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
fuente
14
Tienes que estar atento a cosas como esta. La función de rango se llamará varias veces para cada elemento de la lista. Puede tener pérdidas de memoria desagradables y está generando muchas llamadas a funciones. Casi parece más fácil poner la colección dentro del controlador.
Lucas Holt
1
var a; void 0 === a"verdaderamente" indefinido.
andlrc
1
Puede reducir el golpe de rendimiento almacenando en caché los resultados mediante el patrón de memoria. No estoy seguro de cuál es el costo de la memoria, pero es una mejora. en.wikipedia.org/wiki/Memoization .
Joshua
1
@LucasHolt Finalmente pude actualizar este ejemplo. He agregado una versión optimizada. Todavía tendrá muchas llamadas a funciones, pero reutilizará el primer resultado, lo que lo hace mucho más eficiente.
sqren
93

Nada más que Javascript simple (ni siquiera necesita un controlador):

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

Muy útil al maquetas

Maël Nison
fuente
No funciona en Angular> 1.2.1: Tengo este error en la consola Referencing "constructor" field in Angular expressions is disallowed! Expression: [].constructor(10). Tal vez eso estaba funcionando en una versión anterior de Angular o estoy haciendo algo mal.
AWolf
Creo que probé esto en 1.3 o 1.4 y estuvo bien. Creo que han mejorado el analizador para que no rechace todos los accesos de 'constructor', sino solo los realmente peligrosos (como [].slice.constructor, por ejemplo , el acceso a la Functionevaluación de código).
Maël Nison
OK gracias. Lo probé con 1.2.1 y no funcionaba (la versión incluida en el menú desplegable jsFiddle). Pero con 1.3.15 está funcionando. Ver este jsFiddle .
AWolf
Muy útil para la paginación. Gracias;)
Extraño
69

Se me ocurrió una sintaxis ligeramente diferente que me conviene un poco más y también agrega un límite inferior opcional:

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

que puedes usar como

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

o

<div ng-repeat="n in [20, 29] | makeRange">Do something 20..29: {{n}}</div>
Mormegil
fuente
Esto es genial, pero ¿cómo puede sortear todas las $digestiteraciones cuando usa un valor de ámbito, es decir ng-repeat="n in [1, maxValue] | makeRange":?
pspahn
Los casos de uno y dos param tratan a HighBound de manera diferente, debería ser consistente
Vajk Hermecz
31

Para aquellos nuevos en angularjs. El índice se puede obtener usando $ index.

Por ejemplo:

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

Lo cual, cuando esté usando el práctico filtro de Gloopy, imprima:
haga algo número 0
haga algo número 1
haga algo número 2
haga algo número 3
haga algo número 4
haga algo número 5
haga algo número 6
haga algo número 7
haga algo número 8
hacer algo número 9

Arnoud Sietsema
fuente
77
Es cierto, pero en este caso do something number {{n}}también sería suficiente.
Captncraig
2
Cuando intento ejecutar este código en Chrome, aparece un error en la consola: Error: [$ injector: unpr] errors.angularjs.org/1.2.6/$injector/… ... ... at Wa.statements ( ajax.googleapis.com/ajax/libs/angularjs/1.2.6/… ) <! - ngRepeat: n en [] | rango: 10 -> (Desafortunadamente, todo el error es demasiado largo para caber en la longitud permitida del comentario, así que simplemente copié la primera y la última línea)
Kmeixner
1
He creado un ejemplo de Plunker del código para que pueda compararlo con su código. plnkr.co/edit/tN0156hRfX0M6k9peQ2C?p=preview
Arnoud Sietsema
21

Una forma corta de hacerlo sería utilizar el método _.range () de Underscore.js. :)

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
fuente
1
@jwoodward Él nunca dijo que no era una opción. Mi solución es, con mucho, la más simple y probada gracias a las pruebas unitarias de su repositorio. Siempre puede sacar el js de la biblioteca no minificada e incluirlo como una función.
Michael J. Calkins
44
Es cierto, él nunca dijo eso. Pero generalmente es una mala idea resolver un problema en una biblioteca aplicando otra biblioteca, simplemente evita que aprenda cómo usar la primera biblioteca correctamente. Vaya a otra biblioteca cuando / si no hay una buena solución en la actual.
Erik Honn
55
Ya uso lodash en todas partes, esta fue, con mucho, la solución más fácil. Gracias. Acabo de hacer $ scope.range = _.range, luego es fácil de usar en cualquier plantilla con valores dinámicos de inicio / finalización.
j_walker_dev
2
@ErikHonn: UnderscoreJS resuelve un problema completamente diferente a AngularJS. No hay una buena solución para generar rangos en AngularJS porque ese no es el propósito de la biblioteca.
AlexFoxGill
2
Esto es antiguo, pero seguramente usar una biblioteca diseñada para hacer cosas exactamente como crear un rango, en lugar de abusar de una función en su biblioteca actual de una manera no semántica, ¿es una mejor manera de hacerlo? Seguro que puedes aprender algo, pero terminas con usos distorsionados de cosas diseñadas para otra cosa.
Dan
20

Yo uso mi ng-repeat-rangedirectiva personalizada :

/**
 * 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);
        }
    };
}]);

y el código html es

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

o simplemente

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

o incluso simplemente

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

o solo

<div ng-repeat-range="7">
    Hello 7 times!
</div>
pleerock
fuente
1
Teóricamente agradable, pero la inyección de element.attr('ng-repeat', 'n in [' + rangeString + ']');no funciona ...
Stefan Walther
"Yo suelo...". ¿Realmente has intentado usarlo? El atributo ng-repeat no se compilará.
Peter Hedberg
Gracias Peter, he actualizado el código. Utilicé esta directiva en el pasado, pero aún dejó mi repositorio de código
pleerock el
9

La solución más simple sin código era iniciar una matriz con el rango, y usar el $ index + por mucho que quiera compensar:

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

Definición del método

El siguiente código define un método range()disponible para todo el alcance de su aplicación MyApp. Su comportamiento es muy similar al range()método Python .

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

Uso

Con un parámetro:

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

0, 1, 2,

Con dos parámetros:

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

1, 2, 3, 4,

Con tres parámetros:

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

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

Mathieu Rodic
fuente
6

Puede usar filtros 'after' o 'before' en el módulo angular.filter ( 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>

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

a8m
fuente
6

Sin ningún cambio en su controlador, puede usar esto:

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

¡Disfrutar!

odroz
fuente
he estado luchando más de un día y finalmente esta sintaxis solo funcionó para mí
Agnel Amodia
3

Respuesta más corta: 2 líneas de código

JS (en su controlador AngularJS)

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

... dónde myCountestá el número de estrellas que deberían aparecer en esta ubicación.

Puede usar $indexpara cualquier operación de seguimiento. Por ejemplo, si desea imprimir alguna mutación en el índice, puede poner lo siguiente en div:

{{ ($index + 1) * 0.5 }}
JellicleCat
fuente
2

Usando UnderscoreJS:

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

Si aplica esto $rootScope, estará disponible en todas partes:

<div ng-repeat="x in range(1,10)">
    {{x}}
</div>
AlexFoxGill
fuente
2

Muy simple:

$scope.totalPages = new Array(10);

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

Hola, puedes lograr esto usando html puro usando AngularJS (¡NO se requiere directiva!)

<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
fuente
1

Establecer alcance en el controlador

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

Establecer repetición en archivo de plantilla HTML

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

Una mejora a la solución de @ Mormegil

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

uso

<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

theliberalsurfer
fuente
1

Intenté lo siguiente y funcionó bien para mí:

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

Angular 1.3.6

araghorn
fuente
1

Tarde a la fiesta. Pero terminé haciendo esto:

En tu controlador:

$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
fuente
1

Esta es la respuesta mejorada de jzm (no puedo comentar más, comentaría su respuesta porque incluyó errores). La función tiene un valor de rango de inicio / fin, por lo que es más flexible y ... funciona. Este caso particular es para el día del mes:

$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
fuente
0

Agité esto y vi que podría ser útil para algunos. (Sí, CoffeeScript. Demandarme).

Directiva

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)

Usar:

HTML

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

¿Puede esto ser más simple?

Tengo cuidado con los filtros porque a Angular le gusta reevaluarlos sin una buena razón todo el tiempo, y es un gran cuello de botella si tienes miles de ellos como yo.

Esta directiva incluso observará los cambios en su modelo y actualizará el elemento en consecuencia.

Prathan Thananart
fuente
Esto es bueno, pero ngRepeat mantiene su propio alcance. Por lo tanto, deberá ejecutar la compilación para cada elemento agregado. También necesitará emular animaciones ya que ngRepeat lo hace por usted.
matsko
2
Gracias por contribuir, pero suponiendo que el OP conozca coffeescript, qué es, cómo compilarlo de nuevo a JS, o si alguna vez ha utilizado herramientas de compilación como esa, es una gran exageración. Todos estamos aquí para aprender o ayudar, así que, haz un esfuerzo adicional, convierte a ese cachorro. (¡Has sido demandado!)
Augie Gardner
0
<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>

Si desea lograr esto en html sin ningún controlador o fábrica.

Virendra Singh Rathore
fuente
0

Supongamos que $scope.refernceurles una matriz entonces

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

Esta es la variante más simple: solo use una matriz de enteros ...

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

Stefan
fuente
3
Esto es horrible
Tiffany Lowe
@Stefan, es probable que recibas votos negativos porque la solución no puede usar colecciones codificadas.
Tass