Como fazer um loop pelos itens retornados por uma função com ng-repeat?

114

Eu quero criar divs repetidamente, os itens são objetos retornados por uma função. No entanto, o seguinte código relata erros: 10 $ digest () iterations found. Abortando! jsfiddle está aqui: http://jsfiddle.net/BraveOstrich/awnqm/

<body ng-app>
  <div ng-repeat="entity in getEntities()">
    Hello {{entity.id}}!
  </div>
</body>
Xiao Peng - ZenUML.com
fonte

Respostas:

195

Resposta curta : você realmente precisa dessa função ou pode usar propriedade? http://jsfiddle.net/awnqm/1/

Resposta longa

Para simplificar, descreverei apenas seu caso - ngRepeat para array de objetos. Além disso, omitirei alguns detalhes.

O AngularJS usa verificação suja para detectar alterações. Quando a aplicação é iniciada corre $digestpara $rootScope. $digestfará a travessia em profundidade para a hierarquia do escopo . Todos os escopos têm lista de relógios. Cada relógio tem o último valor (inicialmente initWatchVal). Para cada escopo para todos os relógios o $digestexecuta, obtém o valor atual ( watch.get(scope)) e o compara com watch.last. Se o valor atual não for igual a watch.last(sempre na primeira comparação), $digestdefina dirtycomo true. Quando todos os escopos são processados, ele dirty == true $digestinicia outro percurso em profundidade de $rootScope. $digesttermina quando sujo == falso ou número de travessias == 10. No último caso, o erro "10 $ digest () iterações alcançadas." será registrado.

Agora sobre ngRepeat. Para cada watch.getchamada, ele armazena objetos da coleção (valor de retorno de getEntities) com informações adicionais no cache ( HashQueueMappor hashKey). Para cada watch.getchamada ngRepeattenta obter o objeto por meio hashKeydo cache. Se ele não existir no cache, ngRepeatarmazena em cache, cria novo escopo, coloca objeto sobre ele, cria elemento DOM, etc .

Agora sobre hashKey. Normalmente hashKeyé um número único gerado por nextUid(). Mas pode ser função . hashKeyé armazenado no objeto após a geração para uso futuro.

Por que seu exemplo gera erro : a função getEntities()sempre retorna o array com o novo objeto. Este objeto não tem hashKeye não existe no ngRepeatcache. Assim, ngRepeatcada um watch.getgera um novo escopo para ele com um novo relógio {{entity.id}}. Este relógio primeiro watch.gettem watch.last == initWatchVal. Então watch.get() != watch.last. Então $digestcomeça uma nova travessia. Portanto, ngRepeatcria um novo escopo com o novo relógio. Então ... após 10 travessias você obtém um erro.

Como você pode consertar isso

  1. Não crie novos objetos em cada getEntities()chamada.
  2. Se você precisa criar novos objetos, você pode adicionar hashKeymétodos para eles. Veja este tópico para exemplos.

Espero que as pessoas que conhecem os componentes internos do AngularJS me corrijam se eu estiver errado em algo.

Artem Andreev
fonte
4
+1 obrigado por isso. Eu tive o mesmo problema e não pude usar uma propriedade estática para isso. $$ hashKey realmente deve ser documentado na página ngRepeat do manual IMO.
Michael Moussa
Alguma ideia do que mudou de 1.1.3 para 1.1.4 que afetou isso? Antes de 1.1.4, isso realmente funcionava. Não há nada no changelog sobre isso e não consigo raciocinar qual é a diferença. O comportamento atual faz sentido.
m59
Além disso, verifique se você pudesse: stackoverflow.com/questions/20933261/… Não tenho certeza se minha resposta é o caminho a percorrer ou não ..
m59
2
Portanto, seguir a recomendação Do not create new objects on every getEntities() call.pode ser corrigido facilmente assim:<div ng-repeat="entity in entities = (entities || getEntities())">
przno
2
a solução do meu comentário anterior funciona no caso de quando getEntities()sempre retornar a mesma matriz, se a matriz mudar, você não a obterá nong-repeat
przno
44

Inicialize a matriz fora da repetição

<body ng-app>
   <div ng-init="entities = getEntities()">
       <div ng-repeat="entity in entities">
           Hello {{entity.id}}!
       </div>
   </div>
</body>
Mwayi
fonte
8
Isso não funciona se o getEntities()retornar algo diferente no ciclo de vida do programa. Digamos, por exemplo, que getEntities()desencadeie um $http.get. Quando get finalmente resolver (você fez a chamada AJAX), entitiesjá estará inicializado.
Noite de
3
Dos documentos angularesThe only appropriate use of ngInit is for aliasing special properties of ngRepeat. Besides this case, you should use controllers rather than ngInit to initialize values on a scope.
Blowsie,
Acho que o ponto principal é "inicializar o array fora do repeat" por quaisquer meios ... e @Nighto good call on promises
Mwayi
15

Isso foi relatado aqui e obteve esta resposta:

Seu getter não é idempotente e muda o modelo (gerando um novo array cada vez que é chamado). Isso está forçando angular a continuar chamando-o na esperança de que o modelo eventualmente se estabilize, mas nunca o faz angular desiste e lança uma exceção.

Os valores que o getter retorna são iguais, mas não idênticos, e esse é o problema.

Você pode ver esse comportamento desaparecer se você mover a matriz para fora do controlador principal:

var array = [{id:'angularjs'}];
function Main($scope) {
    $scope.getEntities = function(){return array;};
};

porque agora ele está retornando o mesmo objeto todas as vezes. Pode ser necessário refazer a arquitetura de seu modelo para usar uma propriedade no escopo em vez de uma função:

Nós contornamos isso atribuindo o resultado do método do controlador a uma propriedade e fazendo ng: repeat contra ele.

Dennis
fonte
Usar uma propriedade pode ser a única maneira se a função tiver um parâmetro que muda a cada iteração.
Stephane,
7

Com base no comentário de @przno

<body ng-app>
  <div ng-repeat="item in t = angular.equals(t, getEntities()) ? t : getEntities()">
    Hello {{item.id}}!
  </div>
</body>

A segunda solução BTW @Artem Andreev sugere que não está funcionando no Angular 1.1.4 e superior, enquanto a primeira não resolve o problema. Portanto, por enquanto, esta é a solução menos pontiaguda, sem desvantagens na funcionalidade

Agat
fonte
Você quer dizer item.id? Se entity.id é o que você quer dizer, poderia explicar? Muito Obrigado!
Gerfried
Sim, você está certo. Item.idé o que deveria b. Obrigado
Agat