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>

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>

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

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


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>

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


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>

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>

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>

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,


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


6

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

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

Aproveitar!


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

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>

2

Muito simples:

$scope.totalPages = new Array(10);

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

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>

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>

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


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


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>

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>

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.


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.


0

Suponha que $scope.refernceurlseja uma matriz

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

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


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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.