random orderBy em AngularJS 1.2 retorna erros ‘infdig’

Usando a ordem aleatóriaA técnica de ordenação nesta questão funciona bem no AngularJS 1.1.

 var myApp = angular.module('myApp',[]); function MyCtrl($scope) { $scope.list = ['a', 'b', 'c', 'd', 'e', 'f', 'g']; $scope.random = function() { return 0.5 - Math.random(); } } 

No 1.2, no entanto, ele coloca erros infdig no console e leva muito mais tempo para retornar os resultados classificados: http://jsfiddle.net/mblase75/jVs27/

O erro no console se parece com:

 Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting! Watchers fired in the last 5 iterations: [["fn: $watchCollectionWatch; newVal: 42; oldVal: 36"],["fn: $watchCollectionWatch; newVal: 47; oldVal: 42"],["fn: $watchCollectionWatch; newVal: 54; oldVal: 47"],["fn: $watchCollectionWatch; newVal: 61; oldVal: 54"],["fn: $watchCollectionWatch; newVal: 68; oldVal: 61"]] 

A documentação do orderBy não possui um exemplo de uso de expressões de function, apenas expressões de string. Alguma coisa mudou ou isso é um erro?

Não tenho certeza sobre versões anteriores, mas na versão atual, qualquer expressão observada em um escopo, como a passada para ng-repeat é geralmente avaliada pelo menos duas vezes por resumo. O ciclo de digerir só termina quando os resultados de todas as expressões avaliadas, em todos os escopos de todo o aplicativo Angular, são idênticos entre duas avaliações sucessivas.

Porque cada avaliação de

 
  • {{i}}
  • resulta em chamadas para random () e, portanto, em uma ordem diferente, então Angular continuará avaliando as expressões, até atingir seu limite de 10 iterações de digest e lançar um erro.

    A solução para isso é definir a ordem fora do modelo, no controlador:

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

    E então peça usando o campo por algo como:

     
  • {{i.item}}
  • Isto pode ser visto neste jsfiddle .

    Você pode resolver isso de maneira angular com um filtro personalizado simples. Aqui estou usando o método shuffle de sublinhado que implementa Fischer-Yates.

    Você pode replace as tripas do shuffle pelo seu próprio algoritmo, se preferir.

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

    Agora podemos canalizar nosso array através deste filtro, assim:

     
  • O filtro será chamado uma vez quando o modelo for renderizado.