AngularJS ng-repeat sin elemento html

91

Actualmente estoy usando este código para generar una 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>

Sin embargo, el <div>elemento está causando algunos defectos de representación muy menores en algunos navegadores. Me gustaría saber si hay alguna manera de hacer ng-repeat sin el contenedor div, o algún método alternativo para lograr el mismo efecto.

nehz
fuente
¿De qué problemas de renderizado estás hablando?
Ja͢ck
el último divisor de li parece un poco más grueso como si estuviera apilado (chrome win7). Esto podría ser un problema de CSS, sin embargo, me gustaría saber si esto podría solucionarse en angular. A modo de comparación, knockoutJS permite enlaces sin contenedor usando <! - ->.
nehz
Ya veo, uso phptal en el lado del servidor y tienen una etiqueta tal: block específicamente para este propósito :) supongo que DOM no siempre acepta un nuevo tipo de etiqueta que es más difícil con angular
Ja͢ck
¿También podría actualizar el html para reflejar cómo está usando la htmlAppenddirectiva?
Nick
Hecho ... hice esto hace un tiempo, pero lo recuerdo funcionando
nehz

Respuestas:

104

Como dijo Andy Joslin, estaban trabajando en repeticiones de ng basadas en comentarios, pero aparentemente había demasiados problemas con el navegador . Afortunadamente, AngularJS 1.2 agrega soporte integrado para repetir sin agregar elementos secundarios con las nuevas directivas ng-repeat-starty ng-repeat-end.

Aquí hay un pequeño ejemplo para agregar paginación 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>

Puede encontrar un ejemplo completo de trabajo aquí .

John Lindquist también tiene un video tutorial sobre esto en su excelente página egghead.io .

jmagnusson
fuente
3
¿No requiere esto una impresión de elemento?
hitautodestruct
2
Estoy seriamente confundido sobre lo que esto logra con simplemente <li ng-repeat = "page in [1,2,3,4,5,6]"> <a href="#"> {{page}} </ a > </li> -editar: no importa, creo que lo entiendo
Shadaez
6
Entiendo que solo está tratando de demostrar, ng-repeat-start/endpero este es un ejemplo confuso y el caso de uso incorrecto para él. ng-repeatAquí se debe utilizar un estándar , ya que su código genera un vacío adicional lipara cada elemento. Probablemente deberías mencionar eso en tu publicación.
GFoley83
2
@ GFoley83 tienes razón. Pero parece querer ese elemento extra para cada iteración, algo que no es posible sin él ng-repeat-start. Agregaré la dividerclase html a mi respuesta para ser más claro.
jmagnusson
1
ng-repeat-starty ng-repeat-endson un poco confusos. La documentación de Angular ayuda: https://docs.angularjs.org/api/ng/directive/ngRepeat#special-repeat-start-and-end-points
perilandmishap
30

Sintaxis de enlace sin contenedor KnockoutJS

Por favor, tengan paciencia conmigo un segundo: KnockoutJS ofrece una opción muy conveniente de usar una sintaxis de enlace sin contenedor para su foreachenlace, como se explica en la Nota 4 de la foreachdocumentación de enlace. http://knockoutjs.com/documentation/foreach-binding.html

Como ilustra el ejemplo de la documentación de Knockout, puede escribir su enlace en KnockoutJS de esta manera:

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

Creo que es bastante lamentable que AngularJS no ofrezca este tipo de sintaxis.


Angular ng-repeat-startyng-repeat-end

En la forma en que AngularJS resuelve ng-repeatproblemas, las muestras que encuentro son del tipo que jmagnusson publicó en su (útil) respuesta.

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

Mi pensamiento original al ver esta sintaxis es: ¿en serio? ¿Por qué Angular está forzando todo este marcado adicional con el que no quiero tener nada que ver y que es mucho más fácil en Knockout? Pero luego el comentario de hitautodestruct en la respuesta de jmagnusson comenzó a hacerme preguntarme: ¿qué se está generando con ng-repeat-start y ng-repeat-end en etiquetas separadas?


Una forma más limpia de usar ng-repeat-startyng-repeat-end

Tras la investigación de la afirmación de hitautodestruct, agregar ng-repeat-enduna etiqueta separada es exactamente lo que no quisiera hacer en la mayoría de los casos, porque genera elementos completamente inútiles: en este caso, <li>elementos sin nada en ellos. La lista paginada de Bootstrap 3 diseña los elementos de la lista para que parezca que no generó ningún elemento superfluo, pero cuando inspecciona el html generado, están allí.

Afortunadamente , no necesita hacer mucho para tener una solución más limpia y una menor cantidad de html: simplemente coloque la ng-repeat-enddeclaración en la misma etiqueta html que tiene la extensiónng-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>

Esto da 3 ventajas:

  1. menos etiquetas html para escribir
  2. Angular no genera etiquetas vacías e inútiles
  3. cuando la matriz para repetir está vacía, la etiqueta con ng-repeatno se generará, lo que le brinda la misma ventaja que el enlace sin contenedor de Knockout le brinda en este sentido

Pero todavía hay una forma más limpia

Después de revisar más los comentarios en github sobre este tema para Angular, https://github.com/angular/angular.js/issues/1891 ,
no es necesario usar ng-repeat-starty ng-repeat-endlograr las mismas ventajas. En cambio, bifurcando nuevamente el ejemplo de jmagnusson, podemos simplemente 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>

Entonces, ¿cuándo usar ng-repeat-starty ng-repeat-end? Según la documentación angular , para

... repite una serie de elementos en lugar de solo un elemento padre ...

¡Basta de hablar, muestra algunos ejemplos!

Lo suficientemente justo; este jsbin muestra cinco ejemplos de lo que sucede cuando lo haces y cuando no usasng-repeat-end en la misma etiqueta.

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

mg1075
fuente
11
Parece haber entendido mal el punto de la pregunta. El objetivo es repetir dos o más elementos de la lista simultáneamente, y ni su ejemplo ni la página que vinculó proporcionan una forma de lograrlo. Como ha notado usted mismo, adjuntar ng-repeat-starty ng-repeat-endal mismo elemento es completamente inútil cuando puede usar el valor predeterminado de angular ng-repeaty terminar con él.
Asad Saeeduddin
@Asad - "el punto de la pregunta" no está claramente establecido por el PO. Además, ¿ha inspeccionado la salida DOM renderizada de la respuesta aceptada? No puedo imaginar a nadie que quiera ese tipo de salida, al menos ciertamente no para una lista de paginación de arranque.
mg1075
1
@ mg1075 Sí, el resultado de la respuesta aceptada es inaceptable, por lo que me desplacé hasta aquí. El punto de la pregunta es muy claro: encuentre una manera de aplicar ng-repeato encuentre una alternativa al uso de OP ng-repeatque elimine el divartefacto en el HTML. Su respuesta no hace esto, ya que no hay forma de usarla para los dos lielementos de la pregunta. Si me equivoco aquí, proporcione un marcado de muestra que adapte el código de OP para eliminar el divelemento.
Asad Saeeduddin
@Asad: el punto de la pregunta habría sido más claro si el OP hubiera proporcionado un ejemplo completo del resultado que pretendía. El hecho de que aceptó la respuesta "aceptada", donde la respuesta aceptada utiliza la paginación bootstrap con un marcado esencialmente inadecuado, solo enturbió aún más el problema. El ejemplo 2 en jsbin, si eso es lo que realmente se pretende, resuelve el problema del OP de la forma en que lo hace la respuesta aceptada, pero nadie debería usar ese tipo de marcado para la paginación bootstrap. jsbin.com/eXaPibI/1
mg1075
1
En realidad, acabo de mirar la primera respuesta nuevamente y el marcado generado es exactamente el adecuado para el caso de uso del OP, aunque no satisface mis requisitos. El OP quiere lique se genere un elemento divisor , que es el extra lique ve en su jsbin.
Asad Saeeduddin
6

ngRepeat puede no ser suficiente, sin embargo, puede combinarlo con una directiva personalizada. Puede delegar la tarea de agregar elementos divisores al código si no le importa un poco de jQuery.

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

Una directiva tan simple no necesita realmente un valor de atributo, pero podría brindarle muchas posibilidades, como agregar condicionalmente un divisor de acuerdo con el índice, la longitud, etc. o algo completamente diferente.

Orcun
fuente
2
genial, agregué una directiva personalizada y funciona. Esta guía de video ayudó: youtube.com/watch?v=A6wq16Ow5Ec
nehz
3

Recientemente tuve el mismo problema porque tuve que repetir una colección arbitraria de intervalos e imágenes; tener un elemento alrededor de ellos no era una opción; sin embargo, hay una solución simple, cree una directiva "nula":

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

Luego puede usar una repetición en ese elemento, donde element.url apunta a la plantilla para ese elemento:

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

Esto repetirá cualquier cantidad de plantillas diferentes sin ningún contenedor alrededor.

Nota: hmm, podría haber jurado a ciegas que esto eliminó el elemento di-null al renderizar, pero al verificarlo nuevamente, no lo hace ... aunque resolvió mis problemas de diseño ... más y más curioso ...

Raphael Huerzeler
fuente
replaceestá en desuso desde 2.0.
demalexx
Probé lo anterior, pero la plantilla: "" hace que no se cambie nada. Usé el enlazador de jsfiddle.net/markcoleman/fMP9s/1 y lo modifiqué: link: function (scope, element, attrs) {element [0] .outerHTML = ''; $ compile (element.contents ()) (alcance); }
Lauri Lubi
1

Hay una restricción de directiva de comentarios, pero ngRepeat no la admite (ya que necesita un elemento para repetir).

Creo que vi al equipo angular decir que trabajarían en las repeticiones de comentarios, pero no estoy seguro. Debería abrir un problema en el repositorio. http://github.com/angular/angular.js

Andrew Joslin
fuente
Comentario de 2012. ¿Sigue siendo así? Intenté buscar en su documentación y no pude encontrar ninguna referencia.
Zack S
1

No hay una forma mágica angular de hacer esto, en su caso puede hacer esto, para obtener HTML válido, si está utilizando Bootstrap. Entonces obtendrá el mismo efecto que agregar el divisor li.

Crear una clase:

span.divider {
 display: block;
}

Ahora cambie su código a esto:

<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 en Noruega
fuente
1

para una solución 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>

agregar una directiva 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 una breve explicación,

ng-repeat se une a la <remove> elemento y se como debería, y debido a que hemos usado ng-repeat-start / ng-repeat-end, repite un bloque de html, no solo un elemento.

luego, la directiva de eliminación personalizada coloca los <remove>elementos de inicio y finalización con<!--removed element-->

Clint
fuente
Esto es interesante. Sin embargo, en mis replaceWith(...)resultados de prueba en un nodo de texto, no en un comentario. He descubierto que solo usa element.remove()obras.
artfulrobot