Як використовувати ng-повтор без html-елемента


142

Мені потрібно використовувати ng-repeat(в AngularJS), щоб перелічити всі елементи масиву.

Ускладнення полягає в тому, що кожен елемент масиву перетвориться в один, два або три ряди таблиці.

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

Наприклад, якщо я використовував ng-повтор <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-повторення таким чином, що 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>          

Можливо, вам слід скористатись "замінити: правда"? Дивіться це: stackoverflow.com/questions/11426114 / ...

Крім того, чому ви не можете використовувати ng-повтор на самому tr?

2
@Tommy, оскільки "кожен елемент масиву перетвориться на один, два чи три ряди таблиці". Якби я використовував ng-повторення, trя отримав би один рядок на елемент масиву, наскільки я розумію.
fadedbee

1
Добре, я бачу. Ви не можете просто розрівняти модель, перш ніж використовувати її в ретрансляторі?

@Tommy, ні. 1-3 trс, які генеруються одним елементом масиву, не мають однакової структури.
fadedbee

Відповіді:


66

Оновлення: Якщо ви використовуєте Angular 1.2+, використовуйте ng-repeat-start . Дивіться відповідь @ jmagnusson.

В іншому випадку, як щодо нанесення ng-повтору на tbody? (AFAIK, добре мати кілька <tbody> s в одній таблиці.)

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

62
Хоча це технічно може працювати, дуже невтішно, що відповідь на цей звичайний випадок використання полягає в тому, що вам потрібно ввести довільну (інакше непотрібну) розмітку. У мене така ж проблема (повторні групи рядків - один заголовок TR з одним або декількома дочірніми TR, повторений як група). Тривіально з іншими двигунами шаблону, хакі в кращому випадку з Angular, здається.
Брайан Москау

1
Домовились. Я намагаюсь повторити елементи DT + DD. немає можливості зробити це без додавання недійсного елемента для обгортки
Девід Лін

2
@DavidLin, у Angular v1.2 (кожного разу, коли він з’явиться) ви зможете повторювати над декількома елементами, наприклад, dt та dd: youtube.com/watch?v=W13qDdJDHp8&t=17m28s
Марк Райкок

Це виходить елегантно в KnockoutJS за допомогою синтаксису безконтейнерного потоку управління: knockoutjs.com/documentation/foreach-binding.html . Сподіваюсь, що незабаром Angular зробить щось подібне.
Cory House

3
Ця відповідь застаріла, замість цього використовуйте ng-repeat-start
iwein

73

Щодо AngularJS 1.2 існує директива, яка називає ng-repeat-startсаме те, що ви просите. Дивіться мою відповідь у цьому питанні для опису способів його використання.


Кутовий наздогнав це зараз правильне рішення.
iwein

33

Якщо ви використовуєте 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>

Важливий момент: теги, які використовуються ng-repeat-startта ng-repeat-endвстановлені ng-if="0", не дозволяють вставляти на сторінку. Таким чином внутрішній вміст буде оброблятися точно так, як є у нокаутах (використовуючи команди в <!--...-->), і сміття не буде.


після встановлення ng-if = "0" він видає помилку, не можу знайти відповідні ng-повторення-кінця.
vikky MCTS

19

Ви можете вирівняти дані в контролері:

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>

1
Ні це не так. Викликати функцію всередині виразу ngRepeat категорично не рекомендується
Nik

У моєму випадку, я повинен додати роздільник для кожного елемента , де індекс = 0 і! ng-repeat-start, ng-repeat-endЗламати мій дизайн, тому рішення повинно було створити додаткову змінну додати цей об'єкт роздільник перед кожним елементом і перебирати новий вар: <div class="c477_group"> <div class="c477_group_item" ng-repeat="item in itemsWithSeparator" ng-switch="item.id" ng-class="{'-divider' : item.id == 'SEPARATOR'}"> <div class="c478" ng-switch-when="FAS"/> <div class="c478" ng-switch-when="SEPARATOR" /> <div class="c479" ng-switch-default /> </div> </div>
hermeslm

6

Сказане є правильним, але для більш загальної відповіді недостатньо. Мені потрібно було вкладати ng-повторення, але залишатися на одному рівні 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", який приховує початкові та кінцеві знаки.

Він повинен надрукувати

name1, name1_1, name1_2, name2, name2_1, name2_2,


1

Я хотів би просто прокоментувати, але моєї репутації все ще бракує. Тож я додаю ще одне рішення, яке також вирішує проблему. Я дуже хотів би спростувати заяву @bmoeskau про те, що для вирішення цієї проблеми потрібне рішення "хакі в кращому випадку", і оскільки це нещодавно з'явилося в дискусії, хоча цій посаді є 2 роки, я хотів би додати свою власні два центи:

Як зазначав @btford, ви, схоже, намагаєтесь перетворити рекурсивну структуру в список, тому спочатку слід вирівняти її. Його рішення це робить, але існує думка, що виклик функції всередині шаблону є неелегантним. якщо це правда (чесно кажучи, я не знаю), хіба що потрібно просто виконати функцію в контролері, а не директиву?

так чи інакше, ваш HTML вимагає переліку, тому область, яка надає йому, повинна мати цей список для роботи. ви просто повинні вирівняти структуру всередині вашого контролера. як тільки у вас є масив $ range.rows, ви можете генерувати таблицю одним простим ng-повтором. Ні хакерства, ні елегантності, просто так, як він був розроблений.

Настановам кутів не бракує функціональності. Вони просто змушують вас писати дійсні HTML. У мого колеги виникли подібні проблеми, посилаючись на @bmoeskau на підтримку критики щодо особливостей шаблону / надання. Переглядаючи точну проблему, виявилося, що він просто хотів генерувати відкритий тег, потім закрити тег десь в іншому місці і т. Д. Так само, як і в старі добрі часи, коли ми будемо конспектувати наш 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']

це буде працювати для будь-якої деревоподібної структури, яка має підпункти. Якщо ви хочете весь предмет замість властивості, ви можете ще більше скоротити функцію вирівнювання.

сподівання, що допомагає.


Хороший. Хоча це мені не допомогло прямо, але я відкрив мій погляд на інше рішення. Дуже дякую!
Marian Zburlea

0

для рішення, яке дійсно працює

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-repe прив’язується до <remove>елемента і циклічно як слід, і тому, що ми використовували ng-повторити-почати / ng-повторити-кінець, він циклічно блокує html, а не просто елемент.

то спеціальна директива видалення розміщує <remove>елементи початку та фінішу<!--removed element-->


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

Я думаю, що це справедливо :)


Хоча це працює, він працюватиме лише в тому випадку, якщо масив має три елементи.
btford

Просто переконайтеся, що в масиві є 3 елементи, навіть якщо вони порожні масиви (ng-повторення з порожнім масивом просто нічого не надає).
Ренан Томал Фернандес

7
Моя думка полягала в тому, що ОП, ймовірно, хоче рішення, яке працює для змінної кількості елементів у масиві. Я припускаю, що жорстке кодування "у цьому масиві повинно бути три елементи" в шаблоні було б поганим рішенням.
btford

В останньому коментарі до свого запитання він говорить, що елементи не матимуть однакової структури, тому жорстке кодування кожної структури неминуче.
Ренан Томал Фернандес
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.