AngularJS ng-repeat sem elemento html

91

Atualmente, estou usando este trecho de código para renderizar uma lista:

<ul ng-cloak>
    <div ng-repeat="n in list">
        <li><a href="{{ n[1] }}">{{ n[0] }}</a></li>
        <li class="divider"></i>
    </div>
    <li>Additional item</li> 
</ul>

No entanto, o <div>elemento está causando alguns defeitos de renderização muito pequenos em alguns navegadores. Gostaria de saber se existe uma maneira de repetir o ng sem o contêiner div ou algum método alternativo para obter o mesmo efeito.

nehz
fonte
De que problemas de renderização você está falando?
Ja͢ck
o último divisor li parece um pouco mais grosso como o empilhado (chrome win7). Isso pode ser um problema de css, no entanto, gostaria de saber se isso pode ser corrigido no angular. Para comparação, o knockoutJS permite ligações sem contêiner usando <! - ->.
nehz
Entendo, eu uso phptal no lado do servidor e eles têm uma tag tal: block especificamente para esse propósito :) acho que o DOM nem sempre aceita um novo tipo de tag que é mais difícil com angular
Ja͢ck
Você também poderia atualizar o html para refletir como está usando a htmlAppenddiretiva?
Nick
Pronto ... fiz isso há um tempo, mas me lembro de ter funcionado
nehz,

Respostas:

104

Como Andy Joslin disse, eles estavam trabalhando em repetições de ng com base em comentários, mas aparentemente havia muitos problemas com o navegador . Felizmente, o AngularJS 1.2 adiciona suporte integrado para repetição sem adicionar elementos filho com as novas diretivas ng-repeat-starte ng-repeat-end.

Aqui está um pequeno exemplo para adicionar paginação Bootstrap :

<ul class="pagination">
  <li>
    <a href="#">&laquo;</a>
  </li>
  <li ng-repeat-start="page in [1,2,3,4,5,6]"><a href="#">{{page}}</a></li>
  <li ng-repeat-end class="divider"></li>
  <li>
    <a href="#">&raquo;</a>
  </li>
</ul>

Um exemplo completo de trabalho pode ser encontrado aqui .

John Lindquist também tem um vídeo tutorial sobre isso em sua excelente página egghead.io .

Magnusson
fonte
3
Isso não requer uma impressão de elemento?
hitautodestruct de
2
Estou seriamente confuso sobre o que isso realiza simplesmente <li ng-repeat = "page in [1,2,3,4,5,6]"> <a href="#"> {{page}} </ a > </li> -edit: deixa pra lá, acho que entendi
Shadaez
6
Eu entendo que você está apenas tentando demonstrar, ng-repeat-start/endmas este é um exemplo confuso e o caso de uso errado para ele. Um padrão ng-repeatdeve ser usado aqui, pois seu código renderiza um vazio extra lipara cada item. Você provavelmente deve mencionar isso em sua postagem.
GFoley83
2
@ GFoley83 você está correto. Mas ele parece querer aquele elemento extra para cada iteração, algo que não é possível sem ng-repeat-start. Vou adicionar a dividerclasse html à minha resposta para ser mais claro.
jmagnusson
1
ng-repeat-starte ng-repeat-endsão um pouco confusos. A documentação Angular ajuda: https://docs.angularjs.org/api/ng/directive/ngRepeat#special-repeat-start-and-end-points
perilandmishap
30

Sintaxe de ligação sem contêiner KnockoutJS

Tenha um segundo: KnockoutJS oferece uma opção extremamente conveniente de usar uma sintaxe de vinculação sem contêiner para sua foreachvinculação, conforme discutido na Nota 4 da foreachdocumentação de vinculação. http://knockoutjs.com/documentation/foreach-binding.html

Conforme ilustra o exemplo de documentação do Knockout, você pode escrever sua vinculação em KnockoutJS assim:

<ul>
    <li class="header">Header item</li>
    <!-- ko foreach: myItems -->
        <li>Item <span data-bind="text: $data"></span></li>
    <!-- /ko -->
</ul>

Eu acho que é lamentável que o AngularJS não ofereça esse tipo de sintaxe.


Angular's ng-repeat-starteng-repeat-end

Na maneira do AngularJS de resolver ng-repeatproblemas, os exemplos que encontrei são do tipo que jmagnusson postou em sua resposta (útil).

<li ng-repeat-start="page in [1,2,3,4,5]"><a href="#">{{page}}</a></li>
<li ng-repeat-end></li>

Meu pensamento original ao ver essa sintaxe é: realmente? Por que o Angular está forçando toda essa marcação extra com a qual não quero fazer nada e que é muito mais fácil no Knockout? Mas então o comentário de hitautodestruct na resposta de jmagnusson começou a me fazer pensar: o que está sendo gerado com ng-repeat-start e ng-repeat-end em tags separadas?


Uma maneira mais limpa de usar ng-repeat-starteng-repeat-end

Ao investigar a afirmação de hitautodestruct, adicionar ng-repeat-enduma tag separada é exatamente o que eu não gostaria de fazer na maioria dos casos, porque gera elementos totalmente inúteis: neste caso, <li>itens sem nada neles. A lista paginada do Bootstrap 3 estiliza os itens da lista para que pareça que você não gerou nenhum item supérfluo, mas quando você inspeciona o html gerado, eles estão lá.

Felizmente , você não precisa fazer muito para ter uma solução mais limpa e uma quantidade menor de html: basta colocar a ng-repeat-enddeclaração na mesma tag html que contém ong-repeat-start .

<ul class="pagination">
  <li>
    <a href="#">&laquo;</a>
  </li>    
  <li ng-repeat-start="page in [1,2,3,4,5]" ng-repeat-end><a href="#"></a></li>
  <li>
    <a href="#">&raquo;</a>
  </li>
</ul>

Isso dá 3 vantagens:

  1. menos tags html para escrever
  2. inúteis, tags vazias não são geradas pelo Angular
  3. quando a matriz a ser repetida está vazia, a tag com ng-repeatnão será gerada, dando a você a mesma vantagem que a vinculação sem recipiente do Knockout oferece a esse respeito

Mas ainda há uma maneira mais limpa

Depois de revisar os comentários no github sobre este problema para Angular, https://github.com/angular/angular.js/issues/1891 ,
você não precisa usar ng-repeat-starte ng-repeat-endobter as mesmas vantagens. Em vez disso, bifurcando novamente o exemplo de jmagnusson, podemos simplesmente ir:

<ul class="pagination">
  <li>
    <a href="#">&laquo;</a>
  </li>
  <li ng-repeat="page in [1,2,3,4,5,6]"><a href="#">{{page}}</a></li>
  <li>
    <a href="#">&raquo;</a>
  </li>
</ul>

Então, quando usar ng-repeat-starte ng-repeat-end? De acordo com a documentação angular , para

... repita uma série de elementos em vez de apenas um elemento pai ...

Chega de conversa, mostre alguns exemplos!

Justo; este jsbin percorre cinco exemplos do que acontece quando você faz e quando não usa ng-repeat-endna mesma tag.

http://jsbin.com/eXaPibI/1/

mg1075
fonte
11
Você parece ter entendido mal o ponto da pergunta. O objetivo é repetir dois ou mais itens da lista simultaneamente, e nem seu exemplo nem a página vinculada para fornecer uma maneira de fazer isso. Como você mesmo notou, anexar ng-repeat-starte ng-repeat-endao mesmo elemento é totalmente inútil quando você pode usar o padrão do angular ng-repeate terminar com ele.
Asad Saeeduddin
@Asad - "o ponto da questão" não é claramente definido pelo OP. Além disso, você inspecionou a saída do DOM renderizada da resposta aceita? Não consigo imaginar alguém querendo esse tipo de saída, pelo menos certamente não para uma lista de paginação bootstrap.
mg1075
1
@ mg1075 Sim, a saída da resposta aceita é inaceitável, e é por isso que rolei até aqui. O ponto da questão é muito claro: encontre uma maneira de aplicar ng-repeatou encontrar uma alternativa para o uso do OP ng-repeatque elimine o divartefato no HTML. Sua resposta não faz isso, pois não há como usá-la para os dois lielementos da pergunta. Se eu estiver errado aqui, forneça uma marcação de amostra que adapte o código do OP para eliminar o divelemento.
Asad Saeeduddin de
@Asad - O ponto da questão teria sido mais claro se o OP tivesse fornecido um exemplo final do resultado que ele pretendia. O fato de ele ter aceitado a resposta "aceita", em que a resposta aceita usa paginação bootstrap com marcação essencialmente imprópria, apenas turvou a questão ainda mais. O Exemplo 2 no jsbin, se for o que realmente se pretende, resolve o problema do OP da maneira que a resposta aceita, mas ninguém deve usar esse tipo de marcação para paginação de bootstrap. jsbin.com/eXaPibI/1
mg1075
1
Na verdade, acabei de examinar a primeira resposta novamente e a marcação gerada é exatamente a correta para o caso de uso do OP, embora não atenda aos meus requisitos. O OP deseja que um liitem divisor seja gerado, que é o extra que livocê vê em seu jsbin.
Asad Saeeduddin de
6

ngRepeat pode não ser suficiente, no entanto, você pode combinar isso com uma diretiva personalizada. Você pode delegar a tarefa de adicionar itens divisores ao código se não se importar um pouco com jQuery.

 <li ng-repeat="item in coll" so-add-divide="your exp here"></li>

Uma diretiva tão simples realmente não precisa de um valor de atributo, mas pode dar a você muitas possibilidades, como adicionar condicionalmente um divisor de acordo com o índice, comprimento, etc. ou algo completamente diferente.

orcun
fonte
2
legal, eu adicionei uma diretiva personalizada e funciona. Este guia de vídeo ajudou: youtube.com/watch?v=A6wq16Ow5Ec
nehz
3

Recentemente, tive o mesmo problema em que tive que repetir uma coleção arbitrária de extensões e imagens - ter um elemento ao redor deles não era uma opção - há uma solução simples, no entanto, crie uma diretiva "nula":

app.directive("diNull", function() {
        return {
            restrict: "E",
            replace: true,
            template: ""
        };
    });

Você pode então usar uma repetição nesse elemento, onde element.url aponta para o modelo desse elemento:

<di-null ng-repeat="element in elements" ng-include="element.url" ></di-null>

Isso repetirá qualquer número de modelos diferentes, sem nenhum contêiner em torno deles

Nota: hmm eu poderia ter jurado às cegas, isso removeu o elemento di-null durante a renderização, mas verificá-lo novamente ... ainda resolveu meus problemas de layout ... curioso e curioso ...

Raphael Huerzeler
fonte
replaceestá obsoleto desde 2.0.
demalexx
Tentei o acima, mas o template: "" faz com que nada seja alterado. Usei o linker de jsfiddle.net/markcoleman/fMP9s/1 e modifiquei: link: function (scope, element, attrs) {element [0] .outerHTML = ''; $ compilar (element.contents ()) (escopo); }
Lauri Lubi
1

Há uma restrição de diretiva de comentário, mas ngRepeat não a suporta (uma vez que precisa de um elemento para repetir).

Acho que vi a equipe angular dizer que trabalharia com repetições de comentários, mas não tenho certeza. Você deve abrir um problema para ele no repo. http://github.com/angular/angular.js

Andrew Joslin
fonte
Comentário de 2012. Esse ainda é o caso? Tentei pesquisar na documentação e não consegui encontrar nenhuma referência.
Zack S
1

Não existe uma maneira mágica do Angular de fazer isso; para o seu caso, você pode fazer isso, para obter um HTML válido, se estiver usando o Bootstrap. Então você obterá o mesmo efeito que adicionar o li.divider

Crie uma classe:

span.divider {
 display: block;
}

Agora mude seu código para este:

<ul ng-cloak>
 <li div ng-repeat="n in list">
    <a href="{{ n[1] }}">{{ n[0] }}</a>
    <span class="divider"></span>
 </li>
 <li>Additional item</li>
</ul>
Eduardo na noruega
fonte
1

para uma solução que realmente funcione

html

<remove  ng-repeat-start="itemGroup in Groups" ></remove>
   html stuff in here including inner repeating loops if you want
<remove  ng-repeat-end></remove>

adicione uma diretiva angular.js

//remove directive
(function(){
    var remove = function(){

        return {    
            restrict: "E",
            replace: true,
            link: function(scope, element, attrs, controller){
                element.replaceWith('<!--removed element-->');
            }
        };

    };
    var module = angular.module("app" );
    module.directive('remove', [remove]);
}());

para uma breve explicação,

ng-repeat se liga ao <remove>elemento e faz um loop como deveria, e como usamos ng-repeat-start / ng-repeat-end ele faz um loop em um bloco de html, não apenas em um elemento.

então a diretiva de remoção personalizada coloca os <remove>elementos de início e fim com<!--removed element-->

Clint
fonte
Isto é interessante. No entanto, em meus replaceWith(...)resultados de teste em um nó de texto, não um comentário. Eu encontrei apenas usando element.remove()obras.
artfulrobot