Как использовать ng-repeat без элемента html

Мне нужно использовать ng-repeat (в AngularJS), чтобы отобразить все элементы в массиве.

Усложнение состоит в том, что каждый элемент массива будет преобразовываться в одну, две или три строки таблицы.

Я не могу создать допустимый html, если ng-repeat используется для элемента, поскольку между <tbody> и <tr> не разрешен тип повторяющегося элемента.

Например, если я использовал ng-repeat на <span>, я бы получил:

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

Недопустимый html.

Но мне нужно создать следующее:

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

где первая строка была сгенерирована первым элементом массива, а следующие три - вторым, а пятым и шестым - последним элементом массива.

Как я могу использовать ng-repeat таким образом, чтобы элемент html, с которым он привязан, "исчезает" во время рендеринга?

Или есть другое решение?


Разъяснение: Сгенерированная структура должна выглядеть следующим образом. Каждый элемент массива может генерировать между 1-3 строками таблицы. Ответ должен в идеале поддерживать 0-n строк для элемента массива.

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

Ответ 1

Обновление: Если вы используете Angular 1.2+, используйте ng-repeat-start. См. Ответ @jmagnusson.

В противном случае, как насчет ввода ng-repeat на tbody? (AFAIK, все в порядке, чтобы иметь несколько элементов в одной таблице.)

<tbody ng-repeat="row in array">
  <tr ng-repeat="item in row">
     <td>{{item}}</td>
  </tr>
</tbody>

Ответ 2

В соответствии с AngularJS 1.2 существует директива, называемая ng-repeat-start, которая делает именно то, что вы просите. См. Мой ответ в этом вопросе для описания того, как его использовать.

Ответ 3

Если вы используете ng > 1.2, вот пример использования ng-repeat-start/end без создания ненужных тегов:

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

Ответ 4

Возможно, вы захотите сгладить данные в своем контроллере:

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

И затем в HTML:

<table>
  <tbody>
    <tr ng-repeat="item in flattened()"><td>{{item}}</td></tr>
  </tbody>
</table>

Ответ 5

Вышеприведенное правильно, но для более общего ответа этого недостаточно. Мне нужно было вложить ng-repeat, но оставайтесь на том же уровне html, что означает запись элементов в одном и том же родителе. Массив тегов содержит теги (метки), которые также имеют массив меток. Это фактически дерево.

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

Итак, вот что я в итоге сделал.

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

Обратите внимание на ng-if = "false", который скрывает начальные и конечные divs.

Он должен печатать

имя1, name1_1, name1_2, имя2, NAME2_1, name2_2,

Ответ 6

Я хотел бы просто прокомментировать, но моей репутации все еще не хватает. Поэтому я добавляю другое решение, которое также решает проблему. Я действительно хотел бы опровергнуть выражение, сделанное @bmoeskau о том, что решение этой проблемы требует решения "взлома в лучшем случае", и поскольку это появилось в последнее время в обсуждении, хотя этому сообщению 2 года, я хотел бы добавить свой собственные два цента:

Как отметил @btford, вы пытаетесь превратить рекурсивную структуру в список, поэтому сначала нужно сгладить эту структуру в списке. Его решение делает это, но есть мнение, что вызов функции внутри шаблона является неэлегантным. если это правда (честно говоря, я не знаю), не нужно ли просто выполнять функцию в контроллере, а не директиву?

в любом случае, ваш html требует список, поэтому область, которая его отображает, должна иметь этот список для работы. вам просто нужно сгладить структуру внутри вашего контроллера. если у вас есть массив $scope.rows, вы можете создать таблицу с помощью простого простого ng-повтора. Никаких взломов, неловкости, просто того, как она была разработана для работы.

Угловые директивы не имеют функциональности. Они просто заставляют вас писать допустимый html. У моего коллеги была аналогичная проблема, сославшись на @bmoeskau в поддержку критики в отношении функций шаблонов/рендеринга углов. Если посмотреть на точную проблему, оказалось, что он просто хотел создать открытый тег, затем тег close в другом месте и т.д., Как в старые добрые времена, когда мы будем конкатцировать наш html из строк. Правильно? нет.

как для выравнивания структуры в список, здесь другое решение:

// 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']

это будет работать для любой древовидной структуры, которая имеет подпункты. Если вам нужен весь элемент вместо свойства, вы можете еще больше сократить функцию сглаживания.

надеюсь, что это поможет.

Ответ 7

вы можете использовать функцию сглаживания подчеркивания $scope.myData= _.flatten($scope.myData);

Ответ 8

для решения, которое действительно работает

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>

добавить директиву 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]);
}());

для краткого объяснения,

ng-repeat связывается с элементом <remove> и выполняет циклы, как и должно быть, и поскольку мы использовали ng-repeat-start/ng-repeat-end, он циклически повторяет блок html, а не просто элемент.

затем пользовательская директива удаления помещает начальный и конечный элементы <remove> <!--removed element-->

Ответ 9

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

Я думаю, что это действительно:)