AngularJS For Loop com números e intervalos

252

O Angular fornece algum suporte para um loop for usando números em suas diretivas HTML:

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

Mas se sua variável de escopo incluir um intervalo que tenha um número dinâmico, será necessário criar uma matriz vazia a cada vez.

No controlador

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

No HTML

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

Isso funciona, mas é desnecessário, pois não usaremos a matriz de alcance dentro do loop. Alguém sabe como definir um intervalo ou um valor regular para min / max?

Algo como:

<div data-ng-repeat="i in 1 .. 100">
  do something
</div>
Matsko
fonte
2
Aqui estão mais algumas informações sobre isso. yearofmoo.com/2012/10/...
matsko

Respostas:

281

Ajustei um pouco essa resposta e criei esse violino .

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

Com a repetição usada assim:

<div ng-repeat="n in [] | range:100">
  do something
</div>
Gloopy
fonte
Interessante, não vejo um erro no violino usando o Chrome. Qual navegador?
Gloopy
5
Horrível! ;) Um filtro não é o caminho para fazer isso. Deve ser um novo atributo que funcione com dois valores. O índice atual e o limite superior.
Ian Warburton
10
@IanWarburton Você consegue encontrar uma resposta que atenda aos seus critérios?
dewd
1
@IanWarburton: Existe uma resposta por Уmed abaixo que funciona com uma diretiva, tu não consigo fazê-la funcionar.
Andresch Serj 4/14
1
I necessário um intervalo entre A e B, de modo que esta resposta mexida jsfiddle.net/h9nLc8mk
Marcio
150

Eu vim com uma versão ainda mais simples, para criar um intervalo entre dois números definidos, por exemplo. 5 a 15

Veja a demonstração no 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
fonte
14
Você tem que tomar cuidado com coisas assim. A função de intervalo será chamada várias vezes para cada item da lista. Você pode obter vazamentos de memória desagradáveis ​​e está gerando muitas chamadas de função. Parece quase mais fácil colocar a coleção dentro do controlador.
Lucas Holt
1
var a; void 0 === a"verdadeiramente" indefinido.
andlrc
1
Você pode reduzir o desempenho atingido armazenando em cache os resultados através do padrão do memorizador. Não tenho certeza qual é o custo da memória, mas é uma melhoria. en.wikipedia.org/wiki/Memoization .
217 Joshua Joshua
1
@LucasHolt Finalmente, atualizei este exemplo. Eu adicionei uma versão otimizada. Ele ainda terá muitas chamadas de função, mas reutilizará o primeiro resultado, o que o tornará muito mais eficiente.
Sql # 13/1115
93

Nada além de Javascript simples (você nem precisa de um controlador):

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

Muito útil ao fazer maquete

Maël Nison
fonte
Não está funcionando no Angular> 1.2.1: Eu tenho esse erro no console Referencing "constructor" field in Angular expressions is disallowed! Expression: [].constructor(10). Talvez isso estivesse funcionando em uma versão anterior do Angular ou estou fazendo algo errado.
AWolf
Eu acho que testei isso em 1.3 ou 1.4 e foi bom. Eu acredito que eles melhoraram o analisador para que ele não rejeite todo acesso de 'construtor', mas apenas os realmente perigosos (como [].slice.constructor, por exemplo , o que dá acesso e Function, portanto, a avaliação do código).
Maël Nison 04/04
OK obrigado. Eu testei com o 1.2.1 e ele não estava funcionando (a versão incluída no menu suspenso jsFiddle). Mas com 1.3.15 está funcionando. Veja este jsFiddle .
AWolf
Muito útil para paginação. Obrigado;) #
2700 Stranger
69

Eu vim com uma sintaxe um pouco diferente, que me convém um pouco mais e adiciona um limite 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 você pode usar como

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

ou

<div ng-repeat="n in [20, 29] | makeRange">Do something 20..29: {{n}}</div>
Mormegil
fonte
Isso é ótimo, mas como você contorna todas as $digestiterações ao usar um valor de escopo, ou seja ng-repeat="n in [1, maxValue] | makeRange":?
pspahn
A um e dois casos param deleite highBound diferente, ele deve ser consistente
Vajk Hermecz
31

Para quem é novo no angularjs. O índice pode ser obtido usando $ index.

Por exemplo:

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

O que, quando você estiver usando o prático filtro do Gloopy, imprimirá:
faça algo número 0
faça algo número 1
faça algo número 2
faça algo número 3
faça algo número 4
faça algo número 5
faça algo número 6
faça algo número 6 faça algo número 7
faça algo número 8
faça algo número 9

Arnoud Sietsema
fonte
7
É verdade, mas neste caso do something number {{n}}também seria suficiente.
Captncraig 28/11/2013
2
Quando tento executar esse código no Chrome, recebo um erro no console: Erro: [$ injector: despr] errors.angularjs.org/1.2.6/$injector/… ... ... em Wa.statements ( ajax.googleapis.com/ajax/libs/angularjs/1.2.6/… ) <! - ngRepeat: n em [] | range: 10 -> (Infelizmente, todo o erro é muito longo para caber no tamanho permitido dos comentários, copiei a primeira e a última linha) #
Kmeixner #
1
Eu criei um exemplo do código Plunker para que você possa compará-lo ao seu código. plnkr.co/edit/tN0156hRfX0M6k9peQ2C?p=preview
Arnoud Sietsema
21

Uma maneira curta de fazer isso seria usar o 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
fonte
1
@woodwood Ele nunca disse que não era uma opção. Minha solução é de longe a mais simples e mais testada, graças aos testes de unidade de seu repositório. Você sempre pode retirar os js da biblioteca não minificada e incluí-lo como uma função.
Michael J. Calkins
4
É verdade que ele nunca disse isso. Mas geralmente é uma má idéia resolver um problema em uma biblioteca aplicando outra, apenas impede que você aprenda como usar a primeira biblioteca corretamente. Vá para outra biblioteca quando / se não houver uma boa solução na atual.
Erik Honn
5
Eu já uso lodash em todos os lugares, essa foi de longe a solução mais fácil. Obrigado. Acabei de fazer $ scope.range = _.range, então é fácil de usar em qualquer modelo com valores dinâmicos de início / fim.
9134 jjwalker_dev #
2
@ErikHonn: O UnderscoreJS resolve um problema completamente diferente do AngularJS. Não há uma boa solução para gerar intervalos no AngularJS porque esse não é o objetivo da biblioteca.
AlexFoxGill
2
Isso é antigo, mas certamente usar uma biblioteca projetada para fazer coisas exatamente como criar um intervalo, em vez de abusar de um recurso da sua biblioteca atual de maneira não-semântica, é uma maneira muito melhor de seguir? Claro que você pode aprender alguma coisa, mas você acaba com usos distorcidos de coisas projetadas para outra coisa.
Dan
20

Eu uso minha ng-repeat-rangediretiva 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);
        }
    };
}]);

e código html é

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

ou simplesmente

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

ou simplesmente

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

ou apenas

<div ng-repeat-range="7">
    Hello 7 times!
</div>
pleerock
fonte
1
Teoricamente agradável, mas a injeção de element.attr('ng-repeat', 'n in [' + rangeString + ']');não funciona ...
Stefan Walther
"Eu uso...". Você realmente tentou usá-lo? O atributo ng-repeat não será compilado.
Peter Hedberg
Obrigado Peter, atualizei o código. Eu usei essa diretiva no passado, mas ela ainda deixava meu repositório de código
pleerock
9

A solução mais simples sem código foi iniciar uma matriz com o intervalo e usar o $ index + da maneira que eu quiser compensar:

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

Definição de método

O código abaixo define um método range()disponível para todo o escopo do seu aplicativo MyApp. Seu comportamento é muito semelhante ao 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

Com um parâmetro:

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

0, 1, 2,

Com dois parâmetros:

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

1, 2, 3, 4,

Com três parâmetros:

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

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

Mathieu Rodic
fonte
6

Você pode usar os filtros 'after' ou 'before' no 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
fonte
6

Sem nenhuma alteração no seu controlador, você pode usar isto:

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

Aproveitar!

odroz
fonte
têm-se esforçado mais de um dia e, finalmente, só que desta sintaxe funcionou para mim
Agnel Amodia
3

Resposta mais curta: 2 linhas de código

JS (no seu 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>

... onde myCountestá o número de estrelas que devem aparecer neste local.

Você pode usar $indexpara qualquer operação de rastreamento. Por exemplo, se você quiser imprimir alguma mutação no índice, poderá colocar o seguinte no div:

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

Usando UnderscoreJS:

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

Aplicar isso para $rootScopedisponibilizá-lo em qualquer lugar:

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

Muito simples:

$scope.totalPages = new Array(10);

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

Oi, você pode conseguir isso usando html puro usando AngularJS (nenhuma diretiva é necessária!)

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

Definir escopo no controlador

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

Definir repetição no arquivo de modelo 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
fonte
1

Uma melhoria na solução da @ 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
fonte
1

Eu tentei o seguinte e funcionou muito bem para mim:

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

Angular 1.3.6

Araghorn
fonte
1

Tarde para a festa. Mas acabei fazendo isso:

No seu 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
fonte
1

Esta é a resposta aprimorada do jzm (não posso comentar mais, gostaria de comentar sua resposta porque ela incluiu erros). A função tem um valor de faixa inicial / final, portanto, é mais flexível e ... funciona. Este caso específico é para o dia do mês:

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

Eu agitei isso e vi que poderia ser útil para alguns. (Sim, CoffeeScript. Me processe.)

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>

Isso pode ficar mais simples?

Eu sou cauteloso com os filtros, porque o Angular gosta de reavaliá-los sem uma boa razão o tempo todo, e é um gargalo enorme se você tiver milhares deles como eu.

Essa diretiva até observará as mudanças no seu modelo e atualizará o elemento de acordo.

Prathan Thananart
fonte
Isso é legal, mas o ngRepeat mantém seu próprio escopo. Portanto, você precisará executar a compilação para cada item adicionado. Você também precisará emular animações, já que o ngRepeat faz isso por você.
matsko
2
Obrigado por contribuir, mas presumir que o OP conheça o coffeescript, o que é, como compilá-lo de volta ao JS, ou já usou alguma ferramenta de compilação como essa é um grande esforço. Estamos todos aqui para aprender ou ajudar, então, vá além, converta esse filhote. (Você foi processado!)
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>

Se você deseja conseguir isso em html sem nenhum controlador ou fábrica.

Virendra Singh Rathore
fonte
0

Suponha que $scope.refernceurlseja uma matriz

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

Esta é a variante mais simples: basta usar a matriz de números inteiros ....

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

Stefan
fonte
3
Isto é horrível.
Tiffany Lowe
@ Stefan, é provável que você esteja recebendo votos negativos porque a solução não pode usar coleções codificadas.
Tass