【问题标题】:AngularJS ng-repeat with no html elementAngularJS ng-repeat 没有 html 元素
【发布时间】:2012-10-03 04:32:29
【问题描述】:

我目前正在使用这段代码来呈现一个列表:

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

但是,&lt;div&gt; 元素在某些浏览器上会导致一些非常小的渲染缺陷。 我想知道有没有办法在没有 div 容器的情况下执行 ng-repeat,或者有什么替代方法可以达到同样的效果。

【问题讨论】:

  • 你在说什么渲染问题?
  • 最后一个 li 分隔线看起来有点像堆叠的(chrome win7)。这可能是一个 css 问题,但是,我想知道这是否可以在 Angular 中修复。相比之下,knockoutJS 允许使用 . 进行无容器绑定
  • 我明白了,我在服务器端使用 phptal,他们有一个 tal:block 专门用于此目的的标签 :) 猜测 DOM 并不总是接受一种新的标签,而 Angular 更难
  • 您能否更新 html 以反映您如何使用 htmlAppend 指令?
  • 完成... 不久前做过,但我记得它确实有效

标签: javascript css html angularjs


【解决方案1】:

正如 Andy Joslin 所说,他们正在研究基于评论的 ng-repeats but apparently there were too many browser issues。幸运的是,AngularJS 1.2 使用新指令 ng-repeat-startng-repeat-end 添加了对重复的内置支持,而无需添加子元素。

这是一个添加Bootstrap pagination的小例子:

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

可以在here 找到完整的工作示例。

John Lindquist 还有一个video tutorial of this over at his excellent egghead.io page

【讨论】:

  • 这不需要元素打印吗?
  • 我很困惑这只是简单地完成了
  • {{page}}
  • -edit:没关系,我想我明白了
  • 我知道您只是想演示ng-repeat-start/end,但这是一个令人困惑的示例,并且是错误的用例。此处应使用标准ng-repeat,因为您的代码会为每个项目呈现一个额外的空li。您可能应该在帖子中提及这一点。
  • @GFoley83 你是对的。但他似乎希望每次迭代都有额外的元素,如果没有ng-repeat-start,这是不可能的。我会将divider html 类添加到我的答案中以更清楚。
  • ng-repeat-startng-repeat-end 有点令人困惑。 Angular 文档有帮助:https://docs.angularjs.org/api/ng/directive/ngRepeat#special-repeat-start-and-end-points
  • 【解决方案2】:

    KnockoutJS 无容器绑定语法

    请稍等:KnockoutJS 为其foreach 绑定提供了一个超级方便的选项,它在foreach 绑定文档的注释4 中讨论过。 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 的 ng-repeat-startng-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 中要容易得多?但后来 jmagnusson 的回答中的 hitautodestruct 的评论开始让我想知道:在单独的标签上使用 ng-repeat-start 和 ng-repeat-end 会生成什么?


    使用ng-repeat-startng-repeat-end 的更简洁的方式

    在对 hitautodestruct 的断言进行调查后,将 ng-repeat-end 添加到单独的标签上正是我在大多数情况下想要做的,因为它会生成完全无用的元素:在这种情况下,@ 987654336@ 里面什么都没有的项目。 Bootstrap 3 的分页列表设置了列表项的样式,因此看起来您没有生成任何多余的项,但是当您检查生成的 html 时,它们就在那里。

    幸运的是,您不需要做太多的事情来获得更简洁的解决方案和更短的 html:只需将 ng-repeat-end 声明放在具有 @ 的同一个 html 标记上987654338@.

    <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 的这个问题的 cmets 后,https://github.com/angular/angular.js/issues/1891
    您无需使用ng-repeat-startng-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-startng-repeat-end?根据angular documentation,到

    ...重复一系列元素,而不仅仅是一个父元素...

    说够了,举几个例子吧!

    很公平;这个 jsbin 将介绍五个示例,说明在同一标签上使用和不使用 ng-repeat-end 时会发生什么。

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

    【讨论】:

    • 您似乎误解了问题的重点。目标是同时重复两个或多个列表项,而您的示例和链接的页面都没有提供实现此目的的方法。正如您自己所指出的,当您可以使用 Angular 的默认 ng-repeat 并完成它时,将 ng-repeat-startng-repeat-end 附加到同一个元素是完全没用的。
    • @Asad - OP没有明确说明“问题的重点”。另外,您是否检查了已接受答案的渲染 DOM 输出?我无法想象有人想要那种输出,至少肯定不是为了引导分页列表。
    • @mg1075 是的,接受答案的输出是不可接受的,这就是我一直向下滚动的原因。问题的重点很明确:找到一种方法来应用ng-repeat 或找到OP 使用ng-repeat 的替代方法,以消除HTML 中的div 工件。您的答案没有这样做,因为无法将它用于问题中的两个 li 元素。如果我在这里错了,请提供适应 OP 代码以消除 div 元素的示例标记。
    • @Asad - 如果 OP 提供了他想要的输出的完整示例,问题的重点会更清楚。他接受了“已接受”的答案,其中接受的答案使用带有基本上不正确标记的引导分页,这一事实只会使问题更加混乱。 jsbin 中的示例 2,如果那是真正的意图,以接受的答案的方式解决 OP 的问题,但没有人应该使用这种类型的标记进行引导分页。 jsbin.com/eXaPibI/1
    • 实际上,我只是再次查看了第一个答案,生成的标记完全适合 OP 的用例,即使它不满足我的要求。 OP 希望生成一个分隔符 li 项目,这是您在 jsbin 中看到的额外 li
    【解决方案3】:

    ngRepeat 可能还不够,但是您可以将其与自定义指令结合使用。如果您不介意一点 jQuery,您可以将添加分隔项的任务委托给代码。

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

    这样一个简单的指令实际上并不需要属性值,但可能会给你很多可能性,比如根据索引、长度等有条件地添加分隔符或完全不同的东西。

    【讨论】:

    【解决方案4】:

    我最近遇到了同样的问题,因为我不得不重复任意的跨度和图像集合——在它们周围有一个元素不是一种选择——但是有一个简单的解决方案,创建一个“null”指令:

    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 元素,但再次检查它并没有......仍然解决了我的布局问题......curioser和curioser......

    【讨论】:

    • replace 自 2.0 起已弃用。
    • 我尝试了上面的方法,但是 template: "" 导致没有任何改变。我使用了来自jsfiddle.net/markcoleman/fMP9s/1 的链接器并对其进行了修改:link: function (scope, element, attrs) { element[0].outerHTML = ''; $compile(element.contents())(范围); }
    【解决方案5】:

    寻找真正有效的解决方案

    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 将自身绑定到 &lt;remove&gt; 元素并按其应有的方式循环,并且因为我们使用了 ng-repeat-start / ng-repeat-end 它循环一个 html 块而不仅仅是一个元素。

    然后自定义删除指令将&lt;remove&gt; 开始和结束元素与&lt;!--removed element--&gt; 一起放置

    【讨论】:

    • 这很有趣。但是,在我的测试中,replaceWith(...) 导致文本节点不是评论。我发现只使用element.remove() 就可以了。
    【解决方案6】:

    有一个注释指令限制,但 ngRepeat 不支持它(因为它需要一个元素来重复)。

    我想我看到 Angular 团队说他们会处理评论 ng-repeats,但我不确定。您应该在 repo 上为它打开一个问题。 http://github.com/angular/angular.js

    【讨论】:

    • 2012年的评论。现在还是这样吗?试图在他们的文档中搜索,但找不到任何参考。
    【解决方案7】:

    没有 Angular 神奇的方法可以做到这一点,对于你的情况,如果你使用 Bootstrap,你可以这样做,以获得有效的 HTML。然后你会得到和添加 li.divider 一样的效果

    创建一个类:

    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>
    

    【讨论】:

      猜你喜欢
      相关资源
      最近更新 更多
      热门标签