AngularJS для цикла с числами и диапазонами

252

Angular предоставляет некоторую поддержку цикла for, использующего числа в своих директивах HTML:

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

Но если ваша переменная области действия содержит диапазон с динамическим номером, вам нужно будет каждый раз создавать пустой массив.

В контроллере

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

В HTML

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

Это работает, но это не нужно, так как мы не будем использовать массив диапазона в цикле. Кто-нибудь знает о настройке диапазона или регулятора для минимального / максимального значения?

Что-то вроде:

<div data-ng-repeat="i in 1 .. 100">
  do something
</div>
Мацко
источник
2
Вот еще немного информации об этом. yearofmoo.com/2012/10/…
Мацко

Ответы:

281

Я немного подправил этот ответ и придумал эту скрипку .

Фильтр определяется как:

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

С повторением используется так:

<div ng-repeat="n in [] | range:100">
  do something
</div>
Gloopy
источник
Интересно, что я не вижу ошибки в скрипке с использованием Chrome. Какой браузер?
Gloopy
5
Ужасный! ;) Фильтр не способ сделать это. Это должен быть новый атрибут, который работает с двумя значениями. Текущий индекс и верхняя граница.
Ян Уорбертон
10
@IanWarburton Можете ли вы найти ответ, который соответствует вашим критериям?
роса
1
@IanWarburton: Ниже есть ответ Умеда, который работает с директивой, ты не можешь заставить его работать.
Андреш Серж
1
Я нуждался в диапазоне между A и B, поэтому я настроил этот ответ jsfiddle.net/h9nLc8mk
Marcio
150

Я придумал еще более простую версию для создания диапазона между двумя определенными числами, например. С 5 до 15

Посмотреть демо на JSFiddle

HTML :

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

Контроллер :

$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
источник
14
Вы должны следить за такими вещами. Функция диапазона будет вызываться несколько раз для каждого элемента в списке. Вы можете получить неприятные утечки памяти и генерировать много вызовов функций. Кажется, проще поместить коллекцию в контроллер.
Лукас Холт
1
var a; void 0 === a«по-настоящему» не определено.
andlrc
1
Вы можете уменьшить количество попаданий, кэшируя результаты с помощью шаблона памятки. Не уверен, сколько стоит память, но это улучшение. en.wikipedia.org/wiki/Memoization .
Джошуа
1
@LucasHolt Наконец-то дошло до обновления этого примера. Я добавил оптимизированную версию. У него все еще будет много вызовов функций, но он будет повторно использовать первый результат, что делает его намного более производительным.
квадратный
93

Ничего, кроме простого Javascript (вам даже не нужен контроллер):

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

Очень полезно при макете

Мэл Нисон
источник
Не работает в Angular> 1.2.1: у меня есть эта ошибка в консоли Referencing "constructor" field in Angular expressions is disallowed! Expression: [].constructor(10). Возможно, это работало в предыдущей версии Angular, или я делаю что-то не так.
AWolf
Я думаю, что я проверял это на 1.3 или 1.4, и это было хорошо. Я считаю, что они улучшили синтаксический анализатор, поэтому он не отвергает каждый доступ «конструктора», а только действительно опасные (такие как [].slice.constructor, который дает доступ Function, следовательно, к оценке кода).
Maël Nison
Хорошо, спасибо. Я протестировал его с 1.2.1, и он не работал (версия включена в выпадающий список jsFiddle). Но с 1.3.15 это работает. Смотрите это jsFiddle .
AWolf
Очень полезно для нумерации страниц. Спасибо;)
Незнакомец
69

Я придумал немного другой синтаксис, который мне подходит немного больше и добавляет необязательную нижнюю границу:

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

который вы можете использовать как

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

или

<div ng-repeat="n in [20, 29] | makeRange">Do something 20..29: {{n}}</div>
Mormegil
источник
Это здорово, но как обойти все $digestитерации при использовании значения в области, то есть ng-repeat="n in [1, maxValue] | makeRange":?
pspahn
Случаи с одним и двумя параметрами относятся к highBound по-разному, оно должно быть согласованным
Vajk Hermecz
31

Для новичков в angularjs. Индекс можно получить с помощью $ index.

Например:

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

Что будет, когда вы используете удобный фильтр Gloopy, напечатайте:
сделать что-нибудь номер 0
сделать что-то номер 1
сделать что-то номер 2
сделать что-то номер 3
сделать что-то номер 4
сделать что-то номер 5
сделать что-то номер 6
сделать что-то номер 7
сделать что-то номер 8
сделать что-то номер 9

Арноуд Сиэтзема
источник
7
Правда, но и в этом случае do something number {{n}}тоже будет достаточно.
Captncraig
2
Когда я пытаюсь запустить этот код в Chrome, в консоли появляется сообщение об ошибке: Ошибка: [$ injector: unpr] errors.angularjs.org/1.2.6/$injector/… ... ... at Wa.statements ( ajax.googleapis.com/ajax/libs/angularjs/1.2.6/… ) <! - ngRepeat: n in [] | диапазон: 10 -> (К сожалению, вся ошибка слишком длинная, чтобы уместиться в разрешенную длину комментария, поэтому я просто скопировал первую и последнюю строку)
Kmeixner
1
Я создал пример кода Plunker, чтобы вы могли сравнить его с вашим кодом. plnkr.co/edit/tN0156hRfX0M6k9peQ2C?p=preview
Арно Сиецема
21

Короткий способ сделать это - использовать метод _.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 Он никогда не говорил, что это не вариант. Мое решение на сегодняшний день является самым простым и наиболее проверенным благодаря модульному тестированию их репозитория. Вы всегда можете извлечь js из unminified библиотеки и включить ее в качестве функции.
Майкл Дж. Калкинс
4
Правда, он никогда этого не говорил. Но, как правило, плохая идея решить проблему в одной библиотеке, применяя другую библиотеку, она просто мешает вам научиться правильно использовать первую библиотеку. Обратитесь к другой библиотеке, если / если в текущем нет хорошего решения.
Эрик Хонн
5
Я уже использую lodash везде, этот был безусловно самым простым решением. Спасибо. Я просто сделал $ scope.range = _.range, тогда его легко использовать в любом шаблоне с динамическими начальными / конечными значениями.
j_walker_dev
2
@ErikHonn: UnderscoreJS решает совершенно другую проблему, чем AngularJS. Нет хорошего решения для генерации диапазонов в AngularJS, потому что это не цель библиотеки.
AlexFoxGill
2
Это старое, но, безусловно, использование библиотеки, предназначенной для таких вещей, как создание диапазона, вместо использования функции в вашей текущей библиотеке несемантическим способом - гораздо лучший путь? Конечно, вы можете чему-то научиться, но в итоге вы получите искаженное использование вещей, предназначенных для чего-то другого.
Дан
20

Я использую мою пользовательскую ng-repeat-rangeдирективу:

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

и HTML-код

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

или просто

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

или даже просто

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

или просто

<div ng-repeat-range="7">
    Hello 7 times!
</div>
pleerock
источник
1
Теоретически приятно, но впрыск element.attr('ng-repeat', 'n in [' + rangeString + ']');не работает ...
Stefan Walther
"Я использую...". Вы на самом деле пытались его использовать? Атрибут ng-repeat не будет скомпилирован.
Питер Хедберг
Спасибо Питер, я обновил код. Я использовал эту директиву в прошлом, но она все еще оставляла мой репозиторий кода
pleerock
9

Простейшим решением для кода не было инициировать массив с диапазоном и использовать $ index +, сколько бы я не хотел компенсировать:

<select ng-init="(_Array = []).length = 5;">
    <option ng-repeat="i in _Array track by $index">{{$index+5}}</option>
</select>
Винс
источник
7

Определение метода

Приведенный ниже код определяет метод, range()доступный для всей области вашего приложения MyApp. Его поведение очень похоже на range()метод 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;
    };
}]);

использование

С одним параметром:

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

0, 1, 2,

С двумя параметрами:

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

1, 2, 3, 4,

С тремя параметрами:

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

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

Матье Родик
источник
6

Вы можете использовать фильтры «после» или «до» в модуле 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>

результат: 5, 6, 7, 8, 9, 10

a8m
источник
6

Без каких-либо изменений в вашем контроллере вы можете использовать это:

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

Наслаждайтесь!

odroz
источник
боролись больше суток и, наконец, у меня
сработал
3

Кратчайший ответ: 2 строки кода

JS (в вашем контроллере 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>

... где myCountколичество звезд, которые должны появиться в этом месте.

Вы можете использовать $indexдля любых операций отслеживания. Например, если вы хотите напечатать некоторую мутацию в индексе, вы можете поместить следующее в div:

{{ ($index + 1) * 0.5 }}
JellicleCat
источник
2

Использование UnderscoreJS:

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

Применение этого к $rootScopeделает его доступным везде:

<div ng-repeat="x in range(1,10)">
    {{x}}
</div>
AlexFoxGill
источник
2

Очень простой:

$scope.totalPages = new Array(10);

 <div id="pagination">
    <a ng-repeat="i in totalPages track by $index">
      {{$index+1}}
    </a>   
 </div> 
Эван Ху
источник
2

Привет, вы можете добиться этого, используя чистый HTML, используя AngularJS (Директива не требуется!)

<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

Установить область действия в контроллере

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

Установить повтор в файле шаблона 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
источник
1

Улучшение решения @ 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;
  };
});

использование

<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
источник
1

Я попробовал следующее, и это работало очень хорошо для меня:

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

Угловой 1.3.6

araghorn
источник
1

Поздно на вечеринку. Но в итоге я просто сделал это:

В вашем контроллере:

$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

Это улучшенный ответ jzm (я не могу комментировать, иначе я бы прокомментировал ее / его ответ, потому что он / она включил ошибки). Функция имеет начальное / конечное значение диапазона, поэтому она более гибкая и ... работает. Этот конкретный случай для дня месяца:

$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

Я взбил это и понял, что это может быть полезно для некоторых. (Да, CoffeeScript. Подайте в суд.)

директива

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)

Использовать:

HTML

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

Может ли это быть проще?

Я настороженно отношусь к фильтрам, потому что Angular любит переоценивать их без всякой уважительной причины, и это огромное препятствие, если у вас их тысячи, как и у меня.

Эта директива будет даже следить за изменениями в вашей модели и соответствующим образом обновлять элемент.

Пратан Тананарт
источник
Это хорошо, но ngRepeat поддерживает свою собственную область видимости. Так что вам придется запускать компиляцию для каждого добавленного элемента. Вам также нужно будет эмулировать анимацию, поскольку ngRepeat сделает это за вас.
Мацко
2
Спасибо за помощь, но если предположить, что OP знает coffeescript, что это такое, как скомпилировать его обратно в JS, или когда-либо использовал какие-либо инструменты для сборки, подобные этому, это очень тяжело. Мы все здесь, чтобы учиться или помогать, так что сделайте все возможное, превратите этого щенка. (Вам предъявили иск!)
Оги Гарднер,
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>

Если вы хотите добиться этого в HTML без какого-либо контроллера или фабрики.

Вирандра Сингх Раторе
источник
0

Пусть $scope.refernceurlэто массив тогда

for(var i=0; i<$scope.refernceurl.length; i++){
    $scope.urls+=$scope.refernceurl[i].link+",";
}
Аджай Камбл
источник
-8

Это самый простой вариант: просто используйте массив целых чисел ....

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

Стефан
источник
3
Это ужасно
Тиффани Лоу
@Stefan, вы, вероятно, получаете отрицательные отзывы, потому что решение не может использовать жестко закодированные коллекции.
ТАСС