AngularJS pour boucle avec nombres et plages

252

Angular fournit un certain support pour une boucle for utilisant des nombres dans ses directives HTML:

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

Mais si votre variable d'étendue comprend une plage qui a un nombre dynamique, vous devrez créer un tableau vide à chaque fois.

Dans le contrôleur

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

Dans le HTML

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

Cela fonctionne, mais ce n'est pas nécessaire car nous n'utiliserons pas du tout le tableau de plages dans la boucle. Quelqu'un sait-il de définir une plage ou un régulier pour la valeur min / max?

Quelque chose comme:

<div data-ng-repeat="i in 1 .. 100">
  do something
</div>
matsko
la source
2
Voici quelques informations supplémentaires à ce sujet. yearofmoo.com/2012/10/…
matsko

Réponses:

281

J'ai légèrement modifié cette réponse et j'ai trouvé ce violon .

Filtre défini comme:

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

Avec la répétition utilisée comme ceci:

<div ng-repeat="n in [] | range:100">
  do something
</div>
Gloopy
la source
Intéressant Je ne vois pas d'erreur dans le violon en utilisant Chrome. Quel navigateur?
Gloopy
5
Affreux! ;) Un filtre n'est pas le moyen de le faire. Ce devrait être un nouvel attribut qui fonctionne avec deux valeurs. L'index actuel et la limite supérieure.
Ian Warburton
10
@IanWarburton Pouvez-vous trouver une réponse qui réponde à vos critères?
rosée
1
@IanWarburton: Il y a une réponse de Уmed ci-dessous qui fonctionne avec une directive, tu ne peux pas la faire fonctionner.
Andresch Serj
1
J'avais besoin d'une plage entre A et B, j'ai donc tweeké cette réponse jsfiddle.net/h9nLc8mk
Marcio
150

J'ai proposé une version encore plus simple, pour créer une plage entre deux nombres définis, par exemple. 5 à 15

Voir la démo sur JSFiddle

HTML :

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

Contrôleur :

$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
la source
14
Vous devez faire attention à des choses comme ça. La fonction de plage sera appelée plusieurs fois pour chaque élément de la liste. Vous pouvez obtenir des fuites de mémoire désagréables et vous générez de nombreux appels de fonction. Il semble presque plus facile de mettre la collection à l'intérieur du contrôleur.
Lucas Holt
1
var a; void 0 === a"vraiment" indéfini.
andlrc
1
Vous pouvez réduire la perf perf en mettant en cache les résultats via le modèle de mémorisation. Je ne sais pas quel est le coût de la mémoire, mais c'est une amélioration. en.wikipedia.org/wiki/Memoization .
Joshua
1
@LucasHolt Enfin réussi à mettre à jour cet exemple. J'ai ajouté une version optimisée. Il aura toujours de nombreux appels de fonction, mais il réutilisera le premier résultat, ce qui le rend beaucoup plus performant.
sqren
93

Rien que du Javascript (vous n'avez même pas besoin d'un contrôleur):

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

Très utile lors de la maquette

Maël Nison
la source
Ne fonctionne pas dans Angular> 1.2.1: j'ai cette erreur dans la console Referencing "constructor" field in Angular expressions is disallowed! Expression: [].constructor(10). Peut-être que cela fonctionnait dans une version précédente d'Angular ou que je fais quelque chose de mal.
AWolf
Je pense que j'ai testé cela sur 1.3 ou 1.4 et c'était bien. Je crois qu'ils ont amélioré l'analyseur afin qu'il ne rejette pas tous les accès «constructeurs» mais uniquement ceux vraiment dangereux (comme [].slice.constructor, qui donne accès à Function, donc à l'évaluation du code).
Maël Nison
OK merci. Je l'ai testé avec 1.2.1 et cela ne fonctionnait pas (la version incluse dans la liste déroulante jsFiddle). Mais avec 1.3.15 cela fonctionne. Voir ce jsFiddle .
AWolf
Très utile pour la pagination. Merci;)
Stranger
69

J'ai trouvé une syntaxe légèrement différente qui me convient un peu plus et ajoute également une borne inférieure facultative:

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 vous pouvez utiliser comme

<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
la source
Cela tombe bien, mais comment obtenez-vous autour de toutes les $digestitérations lors de l' utilisation d' une valeur scope, à savoir: ng-repeat="n in [1, maxValue] | makeRange"?
pspahn
Les un et deux cas param traitent highBound différemment, cela devrait être cohérent
Vajk Hermecz
31

Pour ceux qui découvrent angularjs. L'index peut être obtenu en utilisant $ index.

Par exemple:

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

Qui, lorsque vous utilisez le filtre pratique de Gloopy, affiche:
faire quelque chose numéro 0
faire quelque chose numéro 1
faire quelque chose numéro 2
faire quelque chose numéro 3
faire quelque chose numéro 4
faire quelque chose numéro 5
faire quelque chose numéro 6
faire quelque chose numéro 7
faire quelque chose numéro 8
faire quelque chose numéro 9

Arnoud Sietsema
la source
7
Certes, mais dans ce cas, do something number {{n}}cela suffirait également.
captncraig
2
Lorsque j'essaie d'exécuter ce code dans Chrome, j'obtiens une erreur dans la console: Erreur: [$ injector: impr] errors.angularjs.org/1.2.6/$injector/… ... ... à Wa.statements ( ajax.googleapis.com/ajax/libs/angularjs/1.2.6/… ) <! - ngRepeat: n dans [] | plage: 10 -> (Malheureusement, toute l'erreur est trop longue pour tenir dans la longueur de commentaire autorisée, donc je viens de copier la première et la dernière ligne)
Kmeixner
1
J'ai créé un exemple Plunker du code afin que vous puissiez le comparer à votre code. plnkr.co/edit/tN0156hRfX0M6k9peQ2C?p=preview
Arnoud Sietsema
21

Une façon rapide de le faire serait d'utiliser la méthode _.range () d'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
la source
1
@jwoodward Il n'a jamais dit que ce n'était pas une option. Ma solution est de loin la plus simple et la plus testée grâce aux tests unitaires de leur référentiel. Vous pouvez toujours retirer le js de la bibliothèque non réduite et l'inclure en tant que fonction.
Michael J. Calkins
4
Certes, il n'a jamais dit ça. Mais c'est généralement une mauvaise idée de résoudre un problème dans une bibliothèque en appliquant une autre bibliothèque, cela vous empêche simplement d'apprendre comment utiliser correctement la première bibliothèque. Tournez-vous vers une autre bibliothèque quand / s'il n'y a pas de bonne solution dans la bibliothèque actuelle.
Erik Honn
5
J'utilise déjà lodash partout, celui-ci était de loin la solution la plus simple. Je vous remercie. Je viens de faire $ scope.range = _.range, puis c'est facile à utiliser dans n'importe quel modèle avec des valeurs de début / fin dynamiques.
j_walker_dev
2
@ErikHonn: UnderscoreJS résout un problème complètement différent d'AngularJS. Il n'y a pas de bonne solution pour générer des plages dans AngularJS car ce n'est pas le but de la bibliothèque.
AlexFoxGill
2
C'est vieux, mais utiliser une bibliothèque conçue pour faire des choses exactement comme créer une plage, au lieu d'abuser d'une fonctionnalité de votre bibliothèque actuelle de manière non sémantique, est-ce une bien meilleure solution? Bien sûr, vous pouvez apprendre quelque chose, mais vous vous retrouvez avec des utilisations déformées de choses conçues pour autre chose.
Dan
20

J'utilise ma ng-repeat-rangedirective personnalisée :

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

et le code html est

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

ou simplement

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

ou même simplement

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

ou juste

<div ng-repeat-range="7">
    Hello 7 times!
</div>
pleerock
la source
1
Théoriquement sympa, mais l'injection de element.attr('ng-repeat', 'n in [' + rangeString + ']');ne fonctionne pas ...
Stefan Walther
"J'utilise...". Avez-vous réellement essayé de l'utiliser? L'attribut ng-repeat ne sera pas compilé.
Peter Hedberg
Merci Peter, j'ai mis à jour le code. J'ai utilisé cette directive dans le passé, mais elle a toujours quitté mon référentiel de code
pleerock
9

La solution la plus simple sans code était d'initier un tableau avec la plage et d'utiliser le $ index + autant que je veux compenser par:

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

Définition de la méthode

Le code ci-dessous définit une méthode range()disponible pour toute l'étendue de votre application MyApp. Son comportement est très similaire à la range()méthode 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;
    };
}]);

Usage

Avec un paramètre:

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

0, 1, 2,

Avec deux paramètres:

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

1, 2, 3, 4,

Avec trois paramètres:

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

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

Mathieu Rodic
la source
6

Vous pouvez utiliser des filtres 'après' ou 'avant' dans le module 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>

résultat: 5, 6, 7, 8, 9, 10

a8m
la source
6

Sans aucun changement dans votre contrôleur, vous pouvez utiliser ceci:

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

Prendre plaisir!

odroz
la source
ont eu du mal plus d'une journée et finalement seule cette syntaxe a fonctionné pour moi
Agnel Amodia
3

Réponse la plus courte: 2 lignes de code

JS (dans votre contrôleur 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>

... où myCountest le nombre d'étoiles qui devraient apparaître à cet endroit.

Vous pouvez utiliser $indexpour toutes les opérations de suivi. Par exemple, si vous souhaitez imprimer une mutation sur l'index, vous pouvez mettre ce qui suit dans le div:

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

Utilisation de UnderscoreJS:

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

En l'appliquant à, $rootScopeil est disponible partout:

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

Très simple:

$scope.totalPages = new Array(10);

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

Salut, vous pouvez y parvenir en utilisant du HTML pur en utilisant AngularJS (AUCUNE directive n'est requise!)

<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
la source
1

Définir la portée dans le contrôleur

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

Définir la répétition dans le fichier de modèle 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
la source
1

Une amélioration de la solution 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;
  };
});

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

theliberalsurfer
la source
1

J'ai essayé ce qui suit et cela a très bien fonctionné pour moi:

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

Angulaire 1.3.6

araghorn
la source
1

Tard à la fête. Mais j'ai fini par faire ça:

Dans votre contrôleur:

$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
la source
1

Ceci est la réponse améliorée de jzm (je ne peux pas commenter autrement je commenterais sa réponse car il / elle a inclus des erreurs). La fonction a une valeur de plage de début / fin, elle est donc plus flexible et ... elle fonctionne. Ce cas particulier concerne le jour du mois:

$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
la source
0

J'ai fouetté cela et j'ai vu que cela pourrait être utile pour certains. (Oui, CoffeeScript. Sue me.)

Directif

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)

Utiliser:

HTML

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

Cela peut-il être plus simple?

Je me méfie des filtres car Angular aime les réévaluer sans raison valable tout le temps, et c'est un énorme goulot d'étranglement si vous en avez des milliers comme moi.

Cette directive surveillera même les changements dans votre modèle et mettra à jour l'élément en conséquence.

Prathan Thananart
la source
C'est bien, mais ngRepeat maintient sa propre portée. Vous devrez donc exécuter la compilation pour chaque élément ajouté. Vous devrez également émuler des animations car ngRepeat le fait pour vous.
matsko
2
Merci d'avoir contribué, mais en supposant que l'OP connaît le coffeescript, ce que c'est, comment le recompiler vers JS, ou a déjà utilisé des outils de construction comme celui-ci, c'est un énorme tronçon. Nous sommes tous ici pour apprendre ou aider, alors faites un effort supplémentaire, convertissez ce chiot. (Vous avez été poursuivi!)
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 vous voulez y parvenir en html sans aucun contrôleur ni usine.

Virendra Singh Rathore
la source
0

Supposons alors $scope.refernceurlun tableau

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

C'est la variante la plus simple: utilisez simplement un tableau d'entiers ....

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

Stefan
la source
3
C'est horrible.
Tiffany Lowe
@Stefan, vous recevez probablement des downvotes car la solution ne peut pas utiliser de collections codées en dur.
Tass