AngularJS ng-repeat sans élément html

91

J'utilise actuellement ce morceau de code pour afficher une liste:

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

Cependant, l' <div>élément provoque des défauts de rendu très mineurs sur certains navigateurs. Je voudrais savoir s'il existe un moyen de faire le ng-repeat sans le conteneur div, ou une méthode alternative pour obtenir le même effet.

nehz
la source
De quels problèmes de rendu parlez-vous?
Ja͢ck
le dernier diviseur li semble un peu plus épais comme son empilé (chrome win7). Cela pourrait être un problème de CSS, cependant, j'aimerais savoir si cela pourrait être corrigé en angular. À titre de comparaison, knockoutJS autorise les liaisons sans conteneur en utilisant <! - ->.
nehz
Je vois, j'utilise phptal côté serveur et ils ont une balise tal: block spécifiquement à cet effet :) suppose que DOM n'accepte pas toujours un nouveau type de balise qui est plus difficile avec angular
Ja͢ck
Pourriez-vous également mettre à jour le code HTML pour refléter la manière dont vous utilisez la htmlAppenddirective?
Nick
Fait ... je l'ai fait il y a quelque temps, mais je me souviens que cela a fonctionné
nehz

Réponses:

104

Comme Andy Joslin l'a dit, ils travaillaient sur des répétitions basées sur les commentaires, mais apparemment, il y avait trop de problèmes de navigateur . Heureusement, AngularJS 1.2 ajoute la prise en charge intégrée de la répétition sans ajouter d'éléments enfants avec les nouvelles directives ng-repeat-startet ng-repeat-end.

Voici un petit exemple pour ajouter la pagination 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 exemple de travail complet peut être trouvé ici .

John Lindquist a également un didacticiel vidéo à ce sujet sur son excellente page egghead.io .

jmagnusson
la source
3
Cela ne nécessite-t-il pas une impression d'élément?
hitautodestruct
2
Je ne comprends vraiment pas ce que cela accomplit simplement sur <li ng-repeat = "page in [1,2,3,4,5,6]"> <a href="#"> {{page}} </ a > </li> -edit: tant pis je pense que je comprends
Shadaez
6
Je comprends que vous essayez simplement de démontrer, ng-repeat-start/endmais c'est un exemple déroutant et le mauvais cas d'utilisation. Une norme ng-repeatdoit être utilisée ici car votre code rend un vide supplémentaire lipour chaque élément. Vous devriez probablement le mentionner dans votre message.
GFoley83
2
@ GFoley83 vous avez raison. Mais il semble vouloir cet élément supplémentaire pour chaque itération, quelque chose qui n'est pas possible sans ng-repeat-start. J'ajouterai la dividerclasse html à ma réponse pour être plus clair.
jmagnusson
1
ng-repeat-startet ng-repeat-endsont un peu déroutants. La documentation Angular aide: https://docs.angularjs.org/api/ng/directive/ngRepeat#special-repeat-start-and-end-points
perilandmishap
30

Syntaxe de liaison sans conteneur KnockoutJS

Veuillez patienter une seconde: KnockoutJS offre une option ultra-pratique d'utilisation d'une syntaxe de liaison sans conteneur pour sa foreachliaison, comme indiqué dans la note 4 duforeach documentation de liaison. http://knockoutjs.com/documentation/foreach-binding.html

Comme l'illustre l'exemple de documentation de Knockout, vous pouvez écrire votre liaison dans KnockoutJS comme ceci:

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

Je pense que c'est plutôt dommage qu'AngularJS n'offre pas ce type de syntaxe.


Angulaire ng-repeat-start etng-repeat-end

Dans la manière AngularJS de résoudre des ng-repeatproblèmes, les exemples que je rencontre sont du type jmagnusson posté dans sa réponse (utile).

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

Ma pensée originale en voyant cette syntaxe est: vraiment? Pourquoi Angular force-t-il tout ce balisage supplémentaire avec lequel je ne veux rien avoir à faire et qui est tellement plus facile dans Knockout? Mais ensuite, le commentaire de hitautodestruct dans la réponse de jmagnusson a commencé à me faire me demander: qu'est-ce qui est généré avec ng-repeat-start et ng-repeat-end sur des balises séparées?


Une façon plus propre d'utiliser ng-repeat-start etng-repeat-end

Après enquête sur l'assertion de hitautodestruct, ajouter ng-repeat-endune balise séparée est exactement ce que je ne voudrais pas faire dans la plupart des cas, car cela génère des éléments totalement inutiles: dans ce cas,<li> éléments sans rien. La liste paginée de Bootstrap 3 met en forme les éléments de la liste afin qu'il semble que vous n'ayez pas généré d'éléments superflus, mais lorsque vous inspectez le code HTML généré, ils sont là.

Heureusement , vous n'avez pas besoin de faire grand-chose pour avoir une solution plus propre et une quantité plus courte de HTML: il suffit de mettre la ng-repeat-enddéclaration sur la même balise html qui a l'extensionng-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>

Cela donne 3 avantages:

  1. moins de balises html à écrire
  2. inutiles, les balises vides ne sont pas générées par Angular
  3. lorsque le tableau à répéter est vide, la balise avec ng-repeatne sera pas générée, ce qui vous donne le même avantage que la liaison sans conteneur de Knockout vous donne à cet égard

Mais il y a encore un moyen plus propre

Après avoir examiné plus en détail les commentaires dans github sur ce problème pour Angular, https://github.com/angular/angular.js/issues/1891 ,
vous n'avez pas besoin d'utiliser ng-repeat-startet ng-repeat-endd'obtenir les mêmes avantages. Au lieu de cela, en reprenant l'exemple de jmagnusson, nous pouvons simplement aller:

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

Alors, quand utiliser ng-repeat-startet ng-repeat-end? Selon la documentation angulaire , pour

... répéter une série d'éléments au lieu d'un seul élément parent ...

Assez parlé, montrez quelques exemples!

C'est suffisant; ce jsbin parcourt cinq exemples de ce qui se passe lorsque vous faites et lorsque vous n'utilisez pas ng-repeat-endsur la même balise.

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

mg1075
la source
11
Vous semblez avoir mal compris le but de la question. L'objectif est de répéter simultanément deux ou plusieurs éléments de la liste, et ni votre exemple ni la page que vous avez liée ne fournissent un moyen d'accomplir cela. Comme vous l'avez noté vous-même, attacher ng-repeat-startet ng-repeat-endau même élément est totalement inutile lorsque vous pouvez utiliser la valeur par défaut d'angular ng-repeatet en finir avec lui.
Asad Saeeduddin
@Asad - "le point de la question" n'est pas clairement énoncé par le PO. De plus, avez-vous inspecté la sortie DOM rendue de la réponse acceptée? Je ne peux pas imaginer que quiconque veuille ce genre de sortie, du moins certainement pas pour une liste de pagination bootstrap.
mg1075
1
@ mg1075 Oui, la sortie de la réponse acceptée est inacceptable, c'est pourquoi j'ai fait défiler tout le chemin ici. Le point de la question est très clair: trouver un moyen d'appliquer ng-repeatou de trouver une alternative à l'utilisation de OP ng-repeatqui élimine l' divartefact dans le HTML. Votre réponse ne fait pas cela, car il n'y a aucun moyen de l'utiliser pour les deux liéléments de la question. Si je me trompe ici, veuillez fournir un exemple de balisage qui adapte le code d'OP pour éliminer l' divélément.
Asad Saeeduddin
@Asad - Le point de la question aurait été plus clair si l'OP avait fourni un exemple fini de la sortie qu'il voulait. Le fait qu'il ait accepté la réponse «acceptée», où la réponse acceptée utilise la pagination bootstrap avec un balisage essentiellement incorrect, n'a fait qu'embrouiller davantage la question. L'exemple 2 dans le jsbin, si c'est ce qui est vraiment prévu, résout le problème de l'OP comme le fait la réponse acceptée, mais personne ne devrait utiliser ce type de balisage pour la pagination bootstrap. jsbin.com/eXaPibI/1
mg1075
1
En fait, je viens de regarder à nouveau la première réponse et le balisage généré est exactement le bon pour le cas d'utilisation de l'OP, même s'il ne répond pas à mes exigences. L'OP veut qu'un liélément de séparation soit généré, qui est le supplément que livous voyez dans votre jsbin.
Asad Saeeduddin
6

ngRepeat peut ne pas être suffisant, mais vous pouvez combiner cela avec une directive personnalisée. Vous pouvez déléguer la tâche d'ajouter des éléments de séparation au code si cela ne vous dérange pas un peu de jQuery.

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

Une directive aussi simple n'a pas vraiment besoin d'une valeur d'attribut mais pourrait vous donner de nombreuses possibilités comme l'ajout conditionnel d'un diviseur en fonction de l'index, de la longueur, etc. ou quelque chose de complètement différent.

orcun
la source
2
cool j'ai ajouté une directive personnalisée et ça marche. Ce guide vidéo a aidé: youtube.com/watch?v=A6wq16Ow5Ec
nehz
3

J'ai récemment eu le même problème en ce sens que j'ai dû répéter une collection arbitraire de travées et d'images - avoir un élément autour d'eux n'était pas une option - il y a une solution simple cependant, créer une directive "null":

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

Vous pouvez ensuite utiliser une répétition sur cet élément, où element.url pointe vers le modèle de cet élément:

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

Cela répétera n'importe quel nombre de modèles différents sans conteneur autour d'eux

Remarque: hmm, j'aurais pu jurer aveuglément que cela supprimait l'élément di-null lors du rendu, mais en le vérifiant à nouveau, cela ne résout toujours pas mes problèmes de mise en page ... curieux et curieux ...

Raphael Huerzeler
la source
replaceest obsolète depuis 2.0.
demalexx
J'ai essayé ce qui précède, mais template: "" fait que rien n'est changé. J'ai utilisé l'éditeur de liens de jsfiddle.net/markcoleman/fMP9s/1 et je l' ai modifié: link: function (scope, element, attrs) {element [0] .outerHTML = ''; $ compile (element.contents ()) (portée); }
Lauri Lubi
1

Il existe une restriction de directive de commentaire, mais ngRepeat ne la prend pas en charge (car il a besoin d'un élément à répéter).

Je pense avoir vu l'équipe angulaire dire qu'elle travaillerait sur des répétitions de commentaires, mais je ne suis pas sûr. Vous devez ouvrir un problème pour celui-ci sur le repo. http://github.com/angular/angular.js

Andrew Joslin
la source
Commentaire de 2012. Est-ce toujours le cas? J'ai essayé de chercher dans leur documentation et je n'ai trouvé aucune référence.
Zack S
1

Il n'y a pas de moyen magique Angular de le faire, pour votre cas, vous pouvez le faire, pour obtenir du HTML valide, si vous utilisez Bootstrap. Ensuite, vous obtiendrez le même effet que l'ajout du li.divider

Créez une classe:

span.divider {
 display: block;
}

Maintenant, changez votre code en ceci:

<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 Norvège
la source
1

pour une solution qui fonctionne vraiment

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>

ajouter une directive 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]);
}());

pour une brève explication,

ng-repeat se lie au <remove> élément et boucle comme il se doit, et parce que nous avons utilisé ng-repeat-start / ng-repeat-end, il boucle un bloc de html pas seulement un élément.

puis la directive de suppression personnalisée place les <remove>éléments de début et de fin avec<!--removed element-->

Clint
la source
C'est intéressant. Cependant, dans mes tests, les replaceWith(...)résultats sont un nœud de texte et non un commentaire. J'ai trouvé juste en utilisant des element.remove()œuvres.
artfulrobot le