AngularJS数字和范围的循环

258

Angular提供了一些支持,在其HTML指令中使用数字进行for循环:

<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>
这个代码可以工作,但是它是不必要的,因为在循环内部我们根本不会使用range数组。有人知道如何设置范围或常规的最小/最大值吗?
类似以下内容:

像这样:

<div data-ng-repeat="i in 1 .. 100">
  do something
</div>

3
以下是更多关于此的信息。http://www.yearofmoo.com/2012/10/more-angularjs-magic-to-supercharge-your-webapp.html#more-about-loops - matsko
23个回答

289

我稍微修改了这个答案,并创建了这个fiddle

筛选器定义如下:

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

使用repeat的方式如下:

<div ng-repeat="n in [] | range:100">
  do something
</div>

5
糟糕!;) 过滤器不是解决这个问题的方法。应该添加一个新属性,可以使用两个值来实现。当前索引和上限。 - Ian Warburton
10
@IanWarburton,您能否提供符合您要求的答案? - dewd
<div ng-repeat="n in myDynamicVar | range">做些什么</div> 我已经像这样工作了 p in pagesummary.totalPages | paginator - skipy
1
@IanWarburton:下面Уmed的回答使用了一个指令,但我无法让它起作用。 - Andresch Serj
1
我需要一个从A到B的范围,所以我调整了这个答案http://jsfiddle.net/h9nLc8mk/。 - Marcio
显示剩余4条评论

151

我想到了一个更简单的版本,用于创建在两个定义的数字之间的范围,例如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;
};

16
你需要留意这种情况。range函数将会为列表中的每个项目被多次调用。这可能导致内存泄漏和大量的函数调用。把集合放在控制器内似乎更容易一些。 - Lucas Holt
1
var a; void 0 === a 真正的未定义。 - Andreas Louv
1
你可以通过使用记忆化模式缓存结果来减少性能损失。不确定内存成本是多少,但这是一种改进方法。http://en.wikipedia.org/wiki/Memoization。 - Joshua
1
@LucasHolt 终于更新了这个示例。我添加了一个优化版本。它仍然会有许多函数调用,但它将重复使用第一个结果,使其更具性能。 - sqren

101

只需纯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上测试过这个,效果很好。我相信他们已经改进了解析器,所以它不会拒绝每一个“constructor”访问,而只是真正危险的访问(比如“[].slice.constructor”,它可以访问“Function”,从而进行代码评估)。 - Maël Nison
好的,谢谢。我已经用 1.2.1 版本进行了测试,但它无法正常工作(jsFiddle 下拉菜单中包含的版本)。但是使用 1.3.15 版本就可以了。请查看这个 jsFiddle - AWolf
非常适用于分页。谢谢 ;) - Stranger

70

我想出了一个略微不同的语法,更适合我,并添加了一个可选的下限:

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>

这很棒,但是当使用作用域值时,如何避免所有$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
没错,但在这种情况下,“做某事号码{{n}}”也足够了。 - captncraig
2
当我尝试在Chrome中运行此代码时,控制台会出现错误:Error: [$injector:unpr] http://errors.angularjs.org/1.2.6/$injector/unpr?p0=rangeFilterProvider%20%3C-%20rangeFilter ... ...at Wa.statements (https://ajax.googleapis.com/ajax/libs/angularjs/1.2.6/angular.min.js:156:273) <!-- ngRepeat: n in [] | range:10 -->(不幸的是整个错误信息太长了,无法适应允许的评论长度,因此我只复制了第一行和最后一行) - Kmeixner
1
我创建了一个 Plunker 示例,以便您可以将其与您的代码进行比较。 http://plnkr.co/edit/tN0156hRfX0M6k9peQ2C?p=preview - Arnoud Sietsema

21

使用 Underscore.js 的 _.range() 方法是一种简短的方法。 :)

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从未压缩的库中取出并作为函数包含进去。 - Michael J. Calkins
4
没错,他从来没有说过那句话。但是用另一个库解决一个库的问题通常不是个好主意,这只会阻止你学习如何正确使用第一个库。当当前库没有好的解决方案时,再考虑转向另一个库。 - Erik Honn
5
我已经在所有地方使用了 Lodash ,这是迄今为止最简单的解决方案。谢谢。我只需写 $scope.range = _.range,然后就可以在任何模板中使用具有动态起始/结束值的范围了。 - j_walker_dev
2
@ErikHonn:UnderscoreJS解决的问题与AngularJS完全不同。在AngularJS中,没有好的解决方案来生成范围,因为这不是该库的目的。 - Alex
除非有解决方案,否则您可以编写自定义指令或非常简单的过滤器。这就是为什么您应该避免通过应用另一个库来解决问题,因为它会阻止您学习。这并不是说其他库永远不能使用,但您应该首先尝试解决它。当然,如果您已经有了其他原因的下划线,请继续使用,但学习真正的方法仍然很重要:P - Erik Honn
2
这可能有点老旧,但是使用一个专门设计用于创建范围的库,而不是以非语义方式滥用当前库中的某个功能,这绝对是更好的选择。当然,你可能会学到一些东西,但最终你会得到一些被扭曲使用的本来为其他目的设计的东西。 - Dan

21

我使用我的自定义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>

1
理论上很好,但是注入element.attr('ng-repeat', 'n in [' + rangeString + ']');的方式不起作用... - Stefan Walther
我使用...。你是否尝试过使用它?ng-repeat属性将不会被编译。 - Peter Hedberg
谢谢Peter,我已经更新了代码。我以前用过这个指令,但它仍然留在我的代码库中。 - pleerock

9
最简单的无代码解决方案是使用该范围初始化一个数组,并使用$index +想要偏移的数量:
<select ng-init="(_Array = []).length = 5;">
    <option ng-repeat="i in _Array track by $index">{{$index+5}}</option>
</select>

8

方法定义

以下代码定义了一个名为range()的方法,可在应用程序MyApp的整个范围内使用。其行为与 Python 的 range() 方法非常相似。

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,


8

在不改变控制器的情况下,您可以使用以下代码:

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

享受吧!


我已经苦苦挣扎了一天多,最终只有这个语法适用于我。 - Agnel Amodia

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接