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>

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>

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

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


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>

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


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>

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>

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>

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,


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


6

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

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

Cieszyć się!


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

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>

2

Bardzo prosty:

$scope.totalPages = new Array(10);

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

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>

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>

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


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


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>

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>

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.


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.


0

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

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

-8

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

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


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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.