AngularJS ng-repeat senza elemento html


91

Attualmente sto usando questo pezzo di codice per visualizzare un elenco:

<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>

Tuttavia, l' <div>elemento sta causando alcuni difetti di rendering molto minori su alcuni browser. Vorrei sapere che esiste un modo per eseguire ng-repeat senza il contenitore div o un metodo alternativo per ottenere lo stesso effetto.


Di quali problemi di rendering stai parlando?
Ja͢ck

l'ultimo divisore di li sembra un po 'più spesso come il suo stacked (chrome win7). Questo potrebbe essere un problema CSS, tuttavia, vorrei sapere se questo potrebbe essere risolto in angolare. Per fare un confronto, knockoutJS consente associazioni senza contenitore utilizzando <! - ->.
nehz

Vedo, io uso phptal sul lato server e hanno un tag tal: block specifico per questo scopo :) indovina con DOM non sempre accetta un nuovo tipo di tag che è più difficile con
angular

Potreste anche aggiornare l'html per riflettere come state usando la htmlAppenddirettiva?
Nick

Fatto ... l'ho fatto qualche tempo fa, ma ricordo che funzionava
nehz

Risposte:


104

Come ha detto Andy Joslin, stavano lavorando su ripetizioni ng basate sui commenti, ma a quanto pare c'erano troppi problemi con il browser . Fortunatamente AngularJS 1.2 aggiunge il supporto integrato per la ripetizione senza aggiungere elementi figlio con le nuove direttive ng-repeat-starte ng-repeat-end.

Ecco un piccolo esempio per aggiungere l' impaginazione 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>

Un esempio funzionante completo può essere trovato qui .

John Lindquist ha anche un video tutorial su questo nella sua eccellente pagina egghead.io .


3
Questo non richiede la stampa di un elemento?
hitautodestruct

2
Sono seriamente confuso su ciò che si ottiene con la semplice <li ng-repeat = "page in [1,2,3,4,5,6]"> <a href="#"> {{page}} </ a > </li> -edit: non importa, penso di aver capito
Shadaez

6
Capisco che stai solo cercando di dimostrare, ng-repeat-start/endma questo è un esempio confuso e il caso d'uso sbagliato per questo. Uno standard ng-repeatdovrebbe essere usato qui poiché il tuo codice rende un vuoto extra liper ogni elemento. Probabilmente dovresti menzionarlo nel tuo post.
GFoley83

2
@ GFoley83 hai ragione. Ma sembra volere quell'elemento in più per ogni iterazione, qualcosa che non è possibile senza ng-repeat-start. Aggiungerò la dividerclasse html alla mia risposta per essere più chiaro.
jmagnusson

1
ng-repeat-starte ng-repeat-endcreano un po 'di confusione. La documentazione Angular aiuta: https://docs.angularjs.org/api/ng/directive/ngRepeat#special-repeat-start-and-end-points
perilandmishap

30

Sintassi dell'associazione senza contenitore KnockoutJS

Per favore, abbi pazienza un secondo: KnockoutJS offre un'opzione ultra conveniente di utilizzare una sintassi di binding senza contenitore per il suo foreachbinding, come discusso nella Nota 4 delforeach documentazione di binding. http://knockoutjs.com/documentation/foreach-binding.html

Come illustra l'esempio della documentazione di Knockout, puoi scrivere il tuo binding in KnockoutJS in questo modo:

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

Penso che sia piuttosto sfortunato AngularJS non offre questo tipo di sintassi.


Angular ng-repeat-starteng-repeat-end

Nel modo in cui AngularJS risolve i ng-repeatproblemi, i campioni che trovo sono del tipo jmagnusson pubblicato nella sua (utile) risposta.

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

Il mio pensiero originale dopo aver visto questa sintassi è: davvero? Perché Angular sta forzando tutto questo markup extra con cui non voglio avere niente a che fare e che è molto più facile in Knockout? Ma poi il commento di hitautodestruct nella risposta di jmagnusson ha iniziato a farmi chiedere: cosa viene generato con ng-repeat-start e ng-repeat-end su tag separati?


Un modo più pulito di usare ng-repeat-starteng-repeat-end

Dopo aver esaminato l'affermazione di hitautodestruct, l'aggiunta ng-repeat-enddi un tag separato è esattamente ciò che non vorrei fare nella maggior parte dei casi, perché genera elementi completamente privi di utilizzo: in questo caso,<li> elementi senza nulla in essi. L'elenco impaginato di Bootstrap 3 modella gli elementi dell'elenco in modo che sembri non aver generato alcun elemento superfluo, ma quando si ispeziona l'html generato, sono lì.

Fortunatamente , non è necessario fare molto per avere una soluzione più pulita e una quantità minore di html: basta mettere la ng-repeat-enddichiarazione sullo stesso tag html che ha l'estensioneng-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>

Questo offre 3 vantaggi:

  1. meno tag html da scrivere
  2. tag inutili e vuoti non vengono generati da Angular
  3. quando l'array da ripetere è vuoto, il tag con ng-repeatnon verrà generato, dandoti lo stesso vantaggio che l'associazione senza contenitore di Knockout ti offre a questo proposito

Ma c'è ancora un modo più pulito

Dopo aver esaminato ulteriormente i commenti in github su questo problema per Angular, https://github.com/angular/angular.js/issues/1891 ,
non è necessario utilizzare ng-repeat-starte ng-repeat-endper ottenere gli stessi vantaggi. Invece, biforcando di nuovo l'esempio di jmagnusson, possiamo semplicemente andare:

<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>

Quindi quando usare ng-repeat-starte ng-repeat-end? Come da documentazione angolare , a

... ripeti una serie di elementi invece di un solo elemento genitore ...

Basta parlare, mostra alcuni esempi!

Giusto; questo jsbin illustra cinque esempi di cosa succede quando lo fai e quando non lo using-repeat-end sullo stesso tag.

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


11
Sembra che tu abbia frainteso il punto della domanda. L'obiettivo è ripetere due o più voci dell'elenco contemporaneamente e né il tuo esempio né la pagina che hai collegato forniscono un modo per farlo. Come hai notato tu stesso, attaccare ng-repeat-starte ng-repeat-endallo stesso elemento è del tutto inutile quando puoi usare l'impostazione predefinita di angular ng-repeate farla finita.
Asad Saeeduddin

@ Asad - "il punto della domanda" non è chiaramente dichiarato dal PO. Inoltre, hai controllato l'output DOM renderizzato della risposta accettata? Non riesco a immaginare che nessuno desideri quel tipo di output, almeno certamente non per un elenco di impaginazione bootstrap.
mg1075

1
@ mg1075 Sì, l'output della risposta accettata è inaccettabile, motivo per cui ho fatto scorrere fino in fondo qui. Il punto della domanda è molto chiaro: trova un modo per applicare ng-repeato trova un'alternativa all'uso di OP ng-repeatche elimini l' divartefatto nell'HTML. La tua risposta non fa questo, poiché non c'è modo di usarla per i due lielementi nella domanda. Se mi sbaglio qui, fornisci un markup di esempio che adatta il codice di OP per eliminare l' divelemento.
Asad Saeeduddin

@Asad - Il punto della domanda sarebbe stato più chiaro se l'OP avesse fornito un esempio completo dell'output che intendeva. Il fatto che abbia accettato la risposta "accettata", in cui la risposta accettata utilizza l'impaginazione bootstrap con markup essenzialmente improprio, ha solo confuso ulteriormente il problema. L'esempio 2 nel jsbin, se questo è ciò che è veramente inteso, risolve il problema dell'OP nel modo in cui fa la risposta accettata, ma nessuno dovrebbe usare quel tipo di markup per l'impaginazione bootstrap. jsbin.com/eXaPibI/1
mg1075

1
In realtà, ho appena guardato di nuovo la prima risposta e il markup generato è esattamente giusto per il caso d'uso dell'OP, anche se non soddisfa i miei requisiti. L'OP vuole che livenga generato un elemento divisore , che è l'extra liche vedi nel tuo jsbin.
Asad Saeeduddin

6

ngRepeat potrebbe non essere sufficiente, tuttavia puoi combinarlo con una direttiva personalizzata. Potresti delegare il compito di aggiungere elementi divisori al codice se non ti dispiace un po 'di jQuery.

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

Una direttiva così semplice non ha davvero bisogno di un valore di attributo, ma potrebbe darti molte possibilità come l'aggiunta condizionale di un divisore in base a indice, lunghezza, ecc. O qualcosa di completamente diverso.


2
cool ho aggiunto una direttiva personalizzata e funziona. Questa guida video ha aiutato: youtube.com/watch?v=A6wq16Ow5Ec
nehz

3

Recentemente ho avuto lo stesso problema in quanto ho dovuto ripetere una raccolta arbitraria di intervalli e immagini - avere un elemento attorno a loro non era un'opzione - tuttavia esiste una soluzione semplice, creare una direttiva "nulla":

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

Puoi quindi usare una ripetizione su quell'elemento, dove element.url punta al modello per quell'elemento:

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

Questo ripeterà un numero qualsiasi di modelli diversi senza alcun contenitore intorno

Nota: hmm avrei giurato che cieco questo avesse rimosso l'elemento di-null durante il rendering, ma controllandolo di nuovo non ... ha comunque risolto i miei problemi di layout però ... curioso e curioso ...


replaceè deprecato dalla 2.0.
demalexx

Ho provato quanto sopra, ma il modello: "" fa sì che nulla venga modificato. Ho usato il linker da jsfiddle.net/markcoleman/fMP9s/1 e l' ho modificato: link: function (scope, element, attrs) {element [0] .outerHTML = ''; $ compile (element.contents ()) (scope); }
Lauri Lubi

1

C'è una restrizione della direttiva sui commenti, ma ngRepeat non la supporta (poiché necessita di un elemento da ripetere).

Penso di aver visto il team angolare dire che avrebbe lavorato sulle ripetizioni dei commenti, ma non ne sono sicuro. Dovresti aprire un problema per esso nel repo. http://github.com/angular/angular.js


Commento del 2012. È ancora così? Ho provato a cercare nella loro documentazione e non sono riuscito a trovare alcun riferimento.
Zack S

1

Non esiste un modo magico di Angular per farlo, nel tuo caso puoi farlo, per ottenere HTML valido, se stai usando Bootstrap. Quindi otterrai lo stesso effetto dell'aggiunta del divisore li

Crea una classe:

span.divider {
 display: block;
}

Ora cambia il tuo codice in questo:

<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>

1

per una soluzione che funziona davvero

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>

aggiungi una direttiva 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]);
}());

per una breve spiegazione,

ng-repeat si lega al file <remove> all'elemento e come dovrebbe, e poiché abbiamo usato ng-repeat-start / ng-repeat-end, esegue un loop di un blocco di html non solo di un elemento.

quindi la direttiva di rimozione personalizzata inserisce gli <remove>elementi di inizio e fine con<!--removed element-->


Questo è interessante. Tuttavia, nei miei test replaceWith(...)risulta un nodo di testo non un commento, però. Ho scoperto che sto usando solo element.remove()opere.
artfulrobot
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.