Comment utiliser ng-repeat sans élément html

j'ai besoin d'utiliser le ng-repeat (AngularJS) la liste de tous les éléments dans un tableau.

la complication est que chaque élément du tableau se transformera en une, deux ou trois rangées d'une table.

Je ne peux pas créer de html valide, si ng-repeat est utilisé sur un élément, car aucun type d'élément répétitif n'est autorisé entre <tbody> et <tr> .

par exemple, si j'ai utilisé ng-repeat sur <span> , I

<table>
  <tbody>
    <span>
      <tr>...</tr>
    </span>
    <span>
      <tr>...</tr>
      <tr>...</tr>
      <tr>...</tr>
    </span>
    <span>
      <tr>...</tr>
      <tr>...</tr>
    </span>
  </tbody>
</table>          

qui est invalide html.

mais ce que j'ai besoin d'être généré est:

<table>
  <tbody>
    <tr>...</tr>
    <tr>...</tr>
    <tr>...</tr>
    <tr>...</tr>
    <tr>...</tr>
    <tr>...</tr>
  </tbody>
</table>          

où la première rangée a été générée par le premier élément du tableau, les trois suivantes par le deuxième et les cinquième et sixième par le dernier élément du tableau.

Comment puis-je utiliser ng-repeat de manière à ce que l'élément html auquel il est lié "disparaisse" lors du rendu?

ou est-il une autre solution?


Clarification: la structure générée doit ressembler à ce qui suit. Chaque élément de tableau peut générer entre 1-3 lignes de la table. La réponse devrait idéalement prendre en charge des lignes 0-n par élément du tableau.

<table>
  <tbody>
    <!-- array element 0 -->
    <tr>
      <td>One row item</td>
    </tr>
    <!-- array element 1 -->
    <tr>
      <td>Three row item</td>
    </tr>
    <tr>
      <td>Some product details</td>
    </tr>
    <tr>
      <td>Customer ratings</td>
    </tr>
    <!-- array element 2 -->
    <tr>
      <td>Two row item</td>
    </tr>
    <tr>
      <td>Full description</td>
    </tr>
  </tbody>
</table>          
133
demandé sur fadedbee 2012-07-15 13:49:57

8 réponses

mise à jour: si vous utilisez L'angle 1.2+, utilisez ng-repeat-start . Voir la réponse de @jmagnusson.

sinon, pourquoi ne pas mettre la répétition ng sur le corps? (AFAIK, il est correct d'avoir plusieurs s dans une seule table.)

<tbody ng-repeat="row in array">
  <tr ng-repeat="item in row">
     <td>{{item}}</td>
  </tr>
</tbody>
60
répondu Mark Rajcok 2014-06-24 03:19:20

à partir de AngularJS 1.2 il y a une directive appelée ng-repeat-start qui fait exactement ce que vous demandez. Voir ma réponse dans cette question pour une description de la façon de l'utiliser.

68
répondu jmagnusson 2017-05-23 10:31:19

si vous utilisez ng > 1.2, voici un exemple d'utilisation de ng-repeat-start/end sans générer d'étiquettes inutiles:

<html>
  <head>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
    <script>
      angular.module('mApp', []);
    </script>
  </head>
  <body ng-app="mApp">
    <table border="1" width="100%">
      <tr ng-if="0" ng-repeat-start="elem in [{k: 'A', v: ['a1','a2']}, {k: 'B', v: ['b1']}, {k: 'C', v: ['c1','c2','c3']}]"></tr>

      <tr>
        <td rowspan="{{elem.v.length}}">{{elem.k}}</td>
        <td>{{elem.v[0]}}</td>
      </tr>
      <tr ng-repeat="v in elem.v" ng-if="!$first">
        <td>{{v}}</td>
      </tr>

      <tr ng-if="0" ng-repeat-end></tr>
    </table>
  </body>
</html>

le point important: pour les étiquettes utilisées pour ng-repeat-start et ng-repeat-end set ng-if="0" , ne pas laisser être insérée dans la page. De cette façon, le contenu interne sera traité exactement comme il est dans knockoutjs (en utilisant les commandes dans <!--...--> ), et il n'y aura pas de déchets.

26
répondu Duka Árpád 2015-07-10 20:17:32

vous pourriez vouloir aplatir les données dans votre contrôleur:

function MyCtrl ($scope) {
  $scope.myData = [[1,2,3], [4,5,6], [7,8,9]];
  $scope.flattened = function () {
    var flat = [];
    $scope.myData.forEach(function (item) {
      flat.concat(item);
    }
    return flat;
  }
}

puis en HTML:

<table>
  <tbody>
    <tr ng-repeat="item in flattened()"><td>{{item}}</td></tr>
  </tbody>
</table>
18
répondu btford 2012-07-15 19:23:22

ce qui précède est correct, mais pour une réponse plus générale, il ne suffit pas. J'avais besoin d'écrire ng-repeat, mais rester au même niveau html, c'est-à-dire écrire les éléments dans le même parent. Le tableau de tags contient des tags qui ont aussi un tableau de tags. Il est en fait un arbre.

[{ name:'name1', tags: [
  { name: 'name1_1', tags: []},
  { name: 'name1_2', tags: []}
  ]},
 { name:'name2', tags: [
  { name: 'name2_1', tags: []},
  { name: 'name2_2', tags: []}
  ]}
]

alors voici ce que j'ai finalement fait.

<div ng-repeat-start="tag1 in tags" ng-if="false"></div>
    {{tag1}},
  <div ng-repeat-start="tag2 in tag1.tags" ng-if="false"></div>
    {{tag2}},
  <div ng-repeat-end ng-if="false"></div>
<div ng-repeat-end ng-if="false"></div>

Notez le ng-if=" false " qui cache les divs de début et de fin.

il devrait imprimer

nom1,name1_1,name1_2,nom2,name2_1,name2_2,

4
répondu Αλέκος 2015-08-14 12:39:56

je voudrais juste commenter, mais ma réputation fait toujours défaut. Donc j'ajoute une autre solution qui résout aussi le problème. Je voudrais vraiment réfuter la déclaration faite par @bmoeskau que la résolution de ce problème nécessite une solution "hacky au mieux", et puisque cela est venu récemment dans une discussion, même si ce poste a 2 ans, je voudrais ajouter mes propres deux cents:

comme @btford l'a souligné, vous semblez essayer de tourner une structure récursive dans une liste, donc vous devriez aplatir cette structure dans une liste d'abord. Sa solution fait cela, mais il ya une opinion que l'appel de la fonction à l'intérieur du modèle est inélégant. si c'est vrai (honnêtement, je ne sais pas) ne faudrait-il pas simplement exécuter la fonction dans le contrôleur plutôt que la directive?

de toute façon, votre html nécessite une liste, donc la portée qui le rend devrait avoir cette liste pour fonctionner. vous n'avez qu'à aplatir la structure à l'intérieur de votre contrôleur. une fois que vous avez un $champ d'application.rows array, vous pouvez générer la table avec un simple, simple ng-repeat. Pas de piratage, pas d'inélégance, simplement la façon dont il a été conçu pour fonctionner.

Les directives Angulars

ne manquent pas de fonctionnalité. Ils vous forcent simplement à écrire du html valide. Un de mes collègues a eu un problème similaire, citant @bmoeskau à l'appui de la critique sur les angulars des caractéristiques de templage/Rendu. Lorsque l'on regarde le problème exact, il s'est avéré qu'il voulait simplement générer une étiquette ouverte, puis une étiquette fermée ailleurs,etc.. tout comme dans le bon vieux temps où nous concattions notre html à partir de cordes.. droit? aucun.

quant à l'aplatissement de la structure dans une liste, voici une autre solution:

// assume the following structure
var structure = [
    {
        name: 'item1', subitems: [
            {
                name: 'item2', subitems: [
                ],
            }
        ],
    }
];
var flattened = structure.reduce((function(prop,resultprop){
    var f = function(p,c,i,a){
        p.push(c[resultprop]);
        if (c[prop] && c[prop].length > 0 )
          p = c[prop].reduce(f,p);
        return p;
    }
    return f;
})('subitems','name'),[]);

// flattened now is a list: ['item1', 'item2']

cela fonctionnera pour toute structure arborescente comportant des sous-éléments. Si vous voulez l'article entier au lieu d'une propriété, vous pouvez raccourcir encore plus la fonction d'aplatissement.

espère que ça aide.

1
répondu aremes 2015-09-30 10:09:00

vous pouvez utiliser la fonction de soulignement aplati $scope.myData= _.flatten($scope.myData);

0
répondu Maxoizs 2013-04-01 16:07:42
<table>
  <tbody>
    <tr><td>{{data[0].foo}}</td></tr>
    <tr ng-repeat="d in data[1]"><td>{{d.bar}}</td></tr>
    <tr ng-repeat="d in data[2]"><td>{{d.lol}}</td></tr>
  </tbody>
</table>

je pense que c'est valide :)

-1
répondu Renan Tomal Fernandes 2012-07-15 16:31:57