random orderBy in AngularJS 1.2 returns 'infdig' errors

社会主义新天地 提交于 2019-11-27 02:21:37

I'm not sure about previous versions, but in the current version, any expression watched on a scope, such as that passed to ng-repeat is usually evaluated at least twice per digest. The digest cycle only finishes when the results of all evaluated expressions, across all scopes of the entire Angular app, are identical between two successive evaluations.

Because each evaluation of

<li ng-repeat="i in list | orderBy:random">{{i}}</li>

results in calls to random(), and so a different order, then Angular will keep on evaluating the expressions, until it hits its limit of 10 digest iterations, and throws an error.

The solution to this is to set the order outside of the template, in the controller:

$scope.list = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
$scope.rankedList = [];
angular.forEach($scope.list, function(item) {
    $scope.rankedList.push({
        item: item,
        rank: 0.5 - $window.Math.random()
    });
});

And then order using the field by something like:

<li ng-repeat="i in rankedList | orderBy:'rank'">{{i.item}}</li>

This can be seen at this jsfiddle .

You can solve this in an Angular way with a simple custom filter. Here I'm using the underscore shuffle method which implements Fischer-Yates.

You could substitute the guts of the shuffle with your own algorithm if you prefer.

angular.module('shuffle', [])
  .filter('shuffle', function() {
    return function(ary) {
      return _.shuffle(ary);
    }
  });

We can now pipe our array through this filter, like so:

<li ng-repeat='option in options | shuffle'>

The filter will be called once when the template is rendered.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!