AngularJS dla pętli z liczbami i zakresami

252

Angular zapewnia pewne wsparcie dla pętli for używającej liczb w swoich dyrektywach HTML:

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

Ale jeśli zmienna zakresu zawiera zakres, który ma liczbę dynamiczną, musisz za każdym razem utworzyć pustą tablicę.

W kontrolerze

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

W HTML

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

To działa, ale nie jest konieczne, ponieważ w pętli nie będziemy w ogóle używać tablicy zakresów. Czy ktoś wie o ustawianiu zakresu lub regularnej wartości min / maks?

Coś jak:

<div data-ng-repeat="i in 1 .. 100">
  do something
</div>
matsko
źródło
2
Oto więcej informacji na ten temat. yearofmoo.com/2012/10/…
matsko

Odpowiedzi:

281

Ulepszyłem nieco tę odpowiedź i wymyśliłem to skrzypce .

Filtr zdefiniowany jako:

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

Przy takim powtórzeniu:

<div ng-repeat="n in [] | range:100">
  do something
</div>
Gloopy
źródło
Interesujące Nie widzę błędu w skrzypcach podczas korzystania z Chrome. Która przeglądarka
Gloopy
5
Okropny! ;) Filtr nie jest na to sposobem. Powinien to być nowy atrybut, który działa z dwiema wartościami. Aktualny indeks i górna granica.
Ian Warburton,
10
@IanWarburton Czy jesteś w stanie znaleźć odpowiedź, która spełnia twoje kryteria?
rosy
1
@IanWarburton: Poniżej znajduje się odpowiedź autorstwa Уmed, która działa zgodnie z dyrektywą, nie mogę jej zmusić do działania.
Andresch Serj
1
Potrzebowałem zakresu między A i B, więc napisałem na Twitterze tę odpowiedź jsfiddle.net/h9nLc8mk
Marcio
150

Wymyśliłem jeszcze prostszą wersję do tworzenia zakresu między dwiema zdefiniowanymi liczbami, np. Od 5 do 15

Zobacz demo na JSFiddle

HTML :

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

Kontroler :

$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
źródło
14
Musisz uważać na takie rzeczy. Funkcja zasięgu będzie wywoływana wiele razy dla każdego elementu na liście. Możesz dostać nieprzyjemne wycieki pamięci i generujesz wiele wywołań funkcji. Wydaje się, że łatwiej jest umieścić kolekcję w kontrolerze.
Lucas Holt
1
var a; void 0 === a„naprawdę” niezdefiniowany.
andlrc
1
Możesz zmniejszyć trafienie perf poprzez buforowanie wyników za pomocą wzorca memoizer. Nie jestem pewien, jaki jest koszt pamięci, ale jest to poprawa. en.wikipedia.org/wiki/Memoization .
Joshua
1
@LucasHolt Wreszcie udało się zaktualizować ten przykład. Dodałem zoptymalizowaną wersję. Nadal będzie miał wiele wywołań funkcji, ale użyje ponownie pierwszego wyniku, co czyni go znacznie wydajniejszym.
sqren
93

Nic oprócz zwykłego Javascript (nie potrzebujesz nawet kontrolera):

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

Bardzo przydatne przy tworzeniu makiet

Maël Nison
źródło
Nie działa w Angular> 1.2.1: Mam ten błąd w konsoli Referencing "constructor" field in Angular expressions is disallowed! Expression: [].constructor(10). Może to działało w poprzedniej wersji Angulara lub robię coś złego.
AWolf
Myślę, że przetestowałem to na 1.3 lub 1.4 i było w porządku. Uważam, że poprawili parser, aby nie odrzucał dostępu do każdego konstruktora, ale tylko do naprawdę niebezpiecznych (takich jak [].slice.constructor, który daje dostęp Function, a więc do oceny kodu).
Maël Nison,
Ok dzięki. Testowałem to z 1.2.1 i nie działało (wersja zawarta w menu rozwijanym jsFiddle). Ale z 1.3.15 to działa. Zobacz ten jsFiddle .
AWolf
Bardzo przydatne do stronicowania. Dzięki;)
Obcy
69

Wymyśliłem nieco inną składnię, która bardziej mi odpowiada i dodaje opcjonalną dolną granicę:

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

którego możesz użyć jako

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

lub

<div ng-repeat="n in [20, 29] | makeRange">Do something 20..29: {{n}}</div>
Mormegil
źródło
To świetnie, ale jak obejść wszystkie $digestiteracje, używając wartości o zasięgu, tj .: ng-repeat="n in [1, maxValue] | makeRange"?
pspahn
Jeden i dwa przypadki param traktują highBound inaczej, powinno być spójne
Vajk Hermecz 29.09.16
31

Dla nowych użytkowników angularjs. Indeks można uzyskać za pomocą $ index.

Na przykład:

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

Który sprawi, że gdy użyjesz przydatnego filtra Gloopy, wydrukuj:
zrób coś o numerze 0
zrób coś z numerem 1
zrób coś z numerem 2
zrób coś z numerem 3
zrób coś z numerem 4
zrób coś z numerem 5
zrób coś z numerem 6
zrób coś z numerem 7
zrób coś z numerem 8
zrób coś 9

Arnoud Sietsema
źródło
7
To prawda, ale w tym przypadku do something number {{n}}wystarczyłoby.
captncraig
2
Kiedy próbuję uruchomić ten kod w Chrome, pojawia się błąd w konsoli: Błąd: [$ injector: unpr] error.angularjs.org/1.2.6/$injector /... ... ... w Wa.statements ( ajax.googleapis.com/ajax/libs/angularjs/1.2.6/… ) <! - ng Powtórz: n w [] | zakres: 10 -> (Niestety cały błąd jest zbyt długi, aby zmieścić się w dozwolonej długości komentarza, więc właśnie skopiowałem pierwszą i ostatnią linię)
Kmeixner
1
Stworzyłem przykład kodu Plunker, abyś mógł go porównać z kodem. plnkr.co/edit/tN0156hRfX0M6k9peQ2C?p=preview
Arnoud Sietsema
21

Krótkim sposobem na zrobienie tego byłoby użycie metody _.range () 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
źródło
1
@jwoodward Nigdy nie powiedział, że to nie jest opcja. Moje rozwiązanie jest zdecydowanie najprostsze i najczęściej testowane dzięki testom jednostkowym z ich repozytorium. Zawsze możesz wyciągnąć plik js z nieuprawnionej biblioteki i dołączyć go jako funkcję.
Michael J. Calkins
4
To prawda, nigdy tego nie powiedział. Ale ogólnie złym pomysłem jest rozwiązanie problemu w jednej bibliotece przez zastosowanie innej biblioteki, po prostu uniemożliwia to nauczenie się, jak prawidłowo korzystać z pierwszej biblioteki. Przejdź do innej biblioteki, gdy / jeśli nie ma dobrego rozwiązania w bieżącej.
Erik Honn,
5
Używam już lodash wszędzie, ten był zdecydowanie najłatwiejszym rozwiązaniem. Dziękuję Ci. Właśnie zrobiłem $ scope.range = _.range, a następnie jest łatwy w użyciu w dowolnym szablonie z dynamicznymi wartościami początkowymi / końcowymi.
j_walker_dev
2
@ErikHonn: UnderscoreJS rozwiązuje zupełnie inny problem niż AngularJS. Nie ma dobrego rozwiązania do generowania zakresów w AngularJS, ponieważ nie jest to celem biblioteki.
AlexFoxGill
2
Jest to stare, ale z pewnością korzystanie z biblioteki zaprojektowanej do robienia rzeczy takich jak tworzenie zakresu, zamiast nadużywania funkcji w bieżącej bibliotece w sposób nie semantyczny jest o wiele lepszym sposobem? Pewnie możesz się czegoś nauczyć, ale skończysz na zniekształconym użyciu rzeczy zaprojektowanych do czegoś innego.
Dan
20

Używam mojej niestandardowej ng-repeat-rangedyrektywy:

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

a kod HTML to

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

lub po prostu

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

a nawet po prostu

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

Lub tylko

<div ng-repeat-range="7">
    Hello 7 times!
</div>
pleerock
źródło
1
Teoretycznie fajnie, ale zastrzyk element.attr('ng-repeat', 'n in [' + rangeString + ']');nie działa ...
Stefan Walther
"Używam...". Czy rzeczywiście próbowałeś go użyć? Atrybut ng-repeat nie zostanie skompilowany.
Peter Hedberg
Dzięki Peter, zaktualizowałem kod. Korzystałem z tej dyrektywy w przeszłości, ale nadal opuściło moje repozytorium kodów
pleerock
9

Najprostszym rozwiązaniem bez kodu było zainicjowanie tablicy z zakresem i użycie indeksu $ + bez względu na to, jak bardzo chcę to zrównoważyć:

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

Definicja metody

Poniższy kod definiuje metodę range()dostępną dla całego zakresu twojej aplikacji MyApp. Jego zachowanie jest bardzo podobne do range()metody 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;
    };
}]);

Stosowanie

Z jednym parametrem:

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

0, 1, 2,

Z dwoma parametrami:

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

1, 2, 3, 4,

Z trzema parametrami:

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

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

Mathieu Rodic
źródło
6

Możesz użyć filtrów „po” lub „przed” w 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>

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

a8m
źródło
6

Bez żadnych zmian w kontrolerze możesz użyć tego:

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

Cieszyć się!

odroz
źródło
zmagam się dłużej niż jeden dzień i wreszcie tylko ta składnia zadziałała dla mnie
Agnel Amodia
3

Najkrótsza odpowiedź: 2 linie kodu

JS (w twoim kontrolerze 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>

...gdzie myCount jest liczba gwiazd, które powinny pojawić się w tej lokalizacji.

Możesz użyć $indexdo dowolnych operacji śledzenia. Np. Jeśli chcesz wydrukować jakąś mutację w indeksie, możesz umieścić w div:

{{ ($index + 1) * 0.5 }}
JellicleCat
źródło
2

Korzystanie z UnderscoreJS:

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

Zastosowanie tego $rootScopepowoduje, że jest dostępny wszędzie:

<div ng-repeat="x in range(1,10)">
    {{x}}
</div>
AlexFoxGill
źródło
2

Bardzo prosty:

$scope.totalPages = new Array(10);

 <div id="pagination">
    <a ng-repeat="i in totalPages track by $index">
      {{$index+1}}
    </a>   
 </div> 
Evan Hu
źródło
2

Cześć, możesz to osiągnąć za pomocą czystego HTML za pomocą AngularJS (nie jest wymagana żadna dyrektywa!)

<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
źródło
1

Ustaw zakres w kontrolerze

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

Ustaw Powtórz w pliku szablonu 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
źródło
1

Ulepszenie rozwiązania @ 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;
  };
});

stosowanie

<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
źródło
1

Wypróbowałem następujące i zadziałało dla mnie dobrze:

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

Kątowy 1.3.6

araghorn
źródło
1

Późno na imprezę. Ale skończyło to na tym, że:

W twoim kontrolerze:

$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
źródło
1

To jest poprawiona odpowiedź jzm (nie mogę komentować, skomentowałbym jej / jego odpowiedź, ponieważ on / ona zawarł błędy). Funkcja ma wartość zakresu początkowego / końcowego, więc jest bardziej elastyczna i ... działa. Ten szczególny przypadek dotyczy dnia miesiąca:

$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
źródło
0

Podkręciłem to i zobaczyłem, że może to być przydatne dla niektórych. (Tak, CoffeeScript. Pozwij mnie.)

Dyrektywa

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)

Używać:

HTML

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

Czy to może być prostsze?

Obawiam się filtrów, ponieważ Angular lubi je bez końca oceniać bez powodu, a jeśli masz tysiące takich jak ja, to ogromne wąskie gardło.

Ta dyrektywa będzie nawet śledzić zmiany w twoim modelu i odpowiednio aktualizować element.

Prathan Thananart
źródło
To miłe, ale ngRepeat zachowuje własny zakres. Musisz więc uruchomić kompilację dla każdego dodanego elementu. Będziesz także musiał emulować animacje, ponieważ ngRepeat robi to za Ciebie.
matsko
2
Dzięki za wkład, ale zakładanie, że OP zna coffeescript, co to jest, jak go skompilować z powrotem do JS, lub kiedykolwiek używał takich narzędzi do budowania, to ogromny rozrzut. Wszyscy jesteśmy tutaj, aby się uczyć lub pomagać, więc idź o krok dalej, nawróć tego szczeniaka. (Zostałeś pozwany!)
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>

Jeśli chcesz to osiągnąć w html bez żadnego kontrolera lub fabryki.

Virendra Singh Rathore
źródło
0

Załóżmy, że $scope.refernceurljest to tablica

for(var i=0; i<$scope.refernceurl.length; i++){
    $scope.urls+=$scope.refernceurl[i].link+",";
}
Ajay Kamble
źródło
-8

To najprostszy wariant: wystarczy użyć tablicy liczb całkowitych ....

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

Stefan
źródło
3
To jest okropne.
Tiffany Lowe
@Stefan, prawdopodobnie otrzymujesz opinie negatywne, ponieważ rozwiązanie nie może używać kolekcji zakodowanych na stałe.
Tass