AngularJS ng-repeat без элемента html

91

В настоящее время я использую этот фрагмент кода для отображения списка:

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

Однако этот <div>элемент вызывает некоторые очень незначительные дефекты рендеринга в некоторых браузерах. Я хотел бы знать, есть ли способ выполнить ng-repeat без контейнера div или какой-либо альтернативный метод для достижения того же эффекта.

nehz
источник
О каких проблемах рендеринга вы говорите?
Ja͢ck
последний разделитель li кажется немного толще, чем его сложенный (chrome win7). Это может быть проблема css, однако я хотел бы знать, можно ли это исправить в angular. Для сравнения, knockoutJS допускает бесконтейнерные привязки с использованием <! - ->.
nehz
Я вижу, я использую phptal на стороне сервера, и у них есть тег tal: block специально для этой цели :) Предполагаю, что DOM не всегда принимает новый тип тега, который сложнее с angular
Ja͢ck
Не могли бы вы также обновить html, чтобы отразить, как вы используете htmlAppendдирективу?
Nick
Сделано ... сделал это некоторое время назад, но я помню, как это работало
nehz

Ответы:

104

Как сказал Энди Джослин, они работали над ng-повторами на основе комментариев, но, по-видимому, было слишком много проблем с браузером . К счастью, AngularJS 1.2 добавляет встроенную поддержку повторения без добавления дочерних элементов с новыми директивами ng-repeat-startи ng-repeat-end.

Вот небольшой пример добавления разбивки на страницы в 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>

Полный рабочий пример можно найти здесь .

У Джона Линдквиста также есть видеоурок по этому поводу на его превосходной странице egghead.io .

Jmagnusson
источник
3
Разве для этого не требуется печать элемента?
hitautodestruct
2
Я серьезно не понимаю, что это дает просто <li ng-repeat = "page in [1,2,3,4,5,6]"> <a href="#"> {{page}} </ a > </li> -edit: неважно, думаю, я
понял
6
Я понимаю, что вы просто пытаетесь продемонстрировать, ng-repeat-start/endно это запутанный пример и неправильный вариант его использования. Здесь ng-repeatследует использовать стандарт, так как ваш код отображает дополнительный пустой liдля каждого элемента. Вероятно, вам стоит упомянуть об этом в своем сообщении.
GFoley83
2
@ GFoley83 вы правы. Но ему, кажется, нужен этот дополнительный элемент для каждой итерации, без чего невозможно ng-repeat-start. Я добавлю dividerк своему ответу класс html, чтобы было понятнее.
jmagnusson
1
ng-repeat-startи ng-repeat-endнемного сбивают с толку. Документация Angular помогает: https://docs.angularjs.org/api/ng/directive/ngRepeat#special-repeat-start-and-end-points
perilandmishap
30

Синтаксис бесконтейнерной привязки KnockoutJS

Потерпите секунду: KnockoutJS предлагает ультра-удобный вариант использования синтаксиса привязки без контейнера для его foreachпривязки, как описано в примечании 4 foreachдокументации привязки. http://knockoutjs.com/documentation/foreach-binding.html

Как показывает пример документации Knockout, вы можете написать свою привязку в KnockoutJS следующим образом:

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

Мне очень жаль, что AngularJS не предлагает такого синтаксиса.


Angular's ng-repeat-startиng-repeat-end

В способе решения ng-repeatпроблем AngularJS образцы, с которыми я сталкиваюсь, относятся к типу jmagnusson, опубликованному в его (полезном) ответе.

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

Моя первоначальная мысль, увидев этот синтаксис, такова: действительно? Почему Angular заставляет всю эту дополнительную разметку, с которой я не хочу иметь ничего общего, и это намного проще в Knockout? Но затем комментарий hitautodestruct в ответе jmagnusson заставил меня задуматься: что создается с помощью ng-repeat-start и ng-repeat-end в отдельных тегах?


Более чистый способ использования ng-repeat-startиng-repeat-end

После исследования утверждения hitautodestruct, добавление ng-repeat-endв отдельный тег - это именно то, что я бы не хотел делать в большинстве случаев, потому что он генерирует совершенно бесполезные элементы: в данном случае <li>элементы, в которых ничего нет. Разбитый на страницы список Bootstrap 3 стилизует элементы списка так, чтобы казалось, что вы не сгенерировали никаких лишних элементов, но когда вы проверяете сгенерированный html, они есть.

К счастью , вам не нужно много делать, чтобы получить более чистое решение и меньшее количество html: просто поместите ng-repeat-endобъявление в тот же тег html с расширениемng-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>

Это дает 3 преимущества:

  1. меньше HTML-тегов для записи
  2. бесполезные, пустые теги не генерируются Angular
  3. когда массив для повторения пуст, тег с ng-repeatне будет сгенерирован, что дает вам то же преимущество, что бесконтейнерная привязка Knockout дает вам в этом отношении

Но есть еще более чистый способ

После дальнейшего изучения комментариев в github по этой проблеме для Angular, https://github.com/angular/angular.js/issues/1891 ,
вам не нужно использовать ng-repeat-startи ng-repeat-endдля достижения тех же преимуществ. Вместо этого, снова разветвляя пример jmagnusson, мы можем просто пойти:

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

Итак, когда использовать ng-repeat-startи ng-repeat-end? Согласно документации angular , чтобы

... повторить серию элементов вместо одного родительского элемента ...

Хватит говорить, покажи примеры!

Справедливо; Этот jsbin рассматривает пять примеров того, что происходит, когда вы используете и когда не используете один и ng-repeat-endтот же тег.

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

мг1075
источник
11
Похоже, вы неправильно поняли суть вопроса. Цель состоит в том, чтобы повторить два или более элемента списка одновременно, и ни ваш пример, ни страница, на которую вы указали ссылку, не предоставляют способа для этого. Как вы сами заметили, прикрепление ng-repeat-startи ng-repeat-endк одному и тому же элементу совершенно бесполезно, если вы можете использовать значение по умолчанию для angular ng-repeatи покончить с этим.
Асад Саидуддин 08
@Asad - "суть вопроса" четко не сформулирована ОП. Кроме того, вы проверили визуализированный вывод DOM принятого ответа? Я не могу представить, чтобы кто-то захотел получить такой вывод, по крайней мере, конечно, не для списка разбивки на страницы начальной загрузки.
mg1075 08
1
@ mg1075 Да, вывод принятого ответа неприемлем, поэтому я прокрутил его до конца. Суть вопроса очень ясна: найти способ применить ng-repeatили найти альтернативу использованию OP, ng-repeatкоторая устраняет divартефакт в HTML. Ваш ответ этого не делает, так как его нельзя использовать для двух liэлементов вопроса. Если я ошибаюсь, предоставьте образец разметки, которая адаптирует код OP для устранения divэлемента.
Асад Саидуддин 08
@Asad - Суть вопроса была бы яснее, если бы OP предоставил готовый пример вывода, который он намеревался. Тот факт, что он принял «принятый» ответ, где в принятом ответе используется начальная разбивка на страницы с существенно неправильной разметкой, только еще больше запутал проблему. Пример 2 в jsbin, если это то, что действительно задумано, решает проблему OP так же, как принятый ответ, но никто не должен использовать этот тип разметки для разбивки на страницы начальной загрузки. jsbin.com/eXaPibI/1
mg1075 08
1
Фактически, я просто снова посмотрел на первый ответ, и сгенерированная разметка точно подходит для варианта использования OP, даже если она не удовлетворяет моим требованиям. OP хочет, чтобы liбыл сгенерирован разделитель , который liвы видите в своем jsbin.
Асад Саидуддин,
6

ngRepeat может быть недостаточно, однако вы можете комбинировать это с настраиваемой директивой. Вы можете делегировать задачу добавления элементов-разделителей в код, если не возражаете против использования jQuery.

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

Такая простая директива на самом деле не требует значения атрибута, но может дать вам множество возможностей, например, условное добавление разделителя в соответствии с индексом, длиной и т. Д. Или что-то совершенно другое.

Orcun
источник
2
круто, я добавил специальную директиву, и она работает. Помогло это видео-руководство: youtube.com/watch?v=A6wq16Ow5Ec
nehz
3

Недавно у меня была такая же проблема, когда мне пришлось повторить произвольный набор промежутков и изображений - наличие элемента вокруг них не было вариантом - однако есть простое решение, создать "нулевую" директиву:

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

Затем вы можете использовать повторение для этого элемента, где element.url указывает на шаблон для этого элемента:

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

Это будет повторять любое количество разных шаблонов без контейнера вокруг них.

Примечание: хм, я мог поклясться вслепую, что это удалило элемент di-null при рендеринге, но проверка его снова не ... все же решила мои проблемы с макетом ...

Рафаэль Хюрцелер
источник
replaceустарело с 2.0.
demalexx 03
Я пробовал описанное выше, но шаблон: "" ничего не меняет. Я использовал компоновщик из jsfiddle.net/markcoleman/fMP9s/1 и изменил его: link: function (scope, element, attrs) {element [0] .outerHTML = ''; $ compile (element.contents ()) (область действия); }
Лаури Луби
1

Есть ограничение директивы comment, но ngRepeat его не поддерживает (так как ему нужен повторяющийся элемент).

Думаю, я видел, как команда angular говорила, что они будут работать над ng-повторами комментариев, но я не уверен. Вы должны открыть выпуск по нему на репо. http://github.com/angular/angular.js

Эндрю Джослин
источник
Комментарий от 2012 года. Так ли это до сих пор? Пытался поискать в их документации, но не нашел никакой ссылки.
Zack S
1

Нет никакого волшебного способа Angular сделать это, в вашем случае вы можете это сделать, чтобы получить действительный HTML, если вы используете Bootstrap. Тогда вы получите тот же эффект, что и добавление разделителя li.

Создайте класс:

span.divider {
 display: block;
}

Теперь измените свой код на этот:

<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>
Эдуардо в Норвегии
источник
1

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

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 помещает <remove>элементы start и finish с<!--removed element-->

Клинт
источник
Это интересно. Однако в моих replaceWith(...)результатах тестирования текстовый узел не является комментарием. Я нашел просто с помощью element.remove()работ.
artfulrobot